📄 me.txt
字号:
VAR var_r2;
DWORD i1; // =4, 当type == i_Address时,[eax+ebx*4+8888]
DWORD i2; // =8888
};
这个结构INSTR中明确了一条伪码可以有3个操作量,var_w是写操作数,var_r1和var_r2是两
个读操作数。因为INSTR与XCPUCODE结构的不同,在从XCPUCODE到INSTR的转化中,可能会出
现一条指令变成两条甚至多条的情况。比如
mov eax, [ebx+ecx*4+1000]
会变成
i_GetAddr var_w=tem_1, var_r1=EBX, var_r2=ECX, i1=4, i2=1000
i_Readpointto var_w=eax, var_r1=tem_1
会产生一个中间变量tem_1
再比如
push eax
会变成
i_Assign tem_1, eax
i_Sub esp, 4
3. 变量的优化
变量优化是exetoc的主要技术点。
3.1 取变量特征串
以下代码是返回一条指令是如何使用一个变量的:
if (the->bJxx)
return 'J';
PINSTR pinstr = the->pinstr;
BYTE rw = the->rw;
if (pinstr->type == i_Label) return 'L';
if (pinstr->type == i_Return) return 'E';
if (pinstr->type == i_Assign)
{
if (rw == 1) return '5'; //Read
if (rw == 2) return '6'; //Write
if (rw == 3) return '7'; //Read and Write
}
else
{
if (rw == 1) return '1'; //Read
if (rw == 2) return '2'; //Write
if (rw == 3) return '3'; //Read and Write
}
也就是说,由几个字母表示了指令对一个变量的使用方法,
有"JLE567123"种可能。J代表一个跳转,L代表一个label即跳转目的,
E代表函数结束,1读2写3是读写。5代表赋值给另外一个量,6代表把另一个
量赋值给我。
这样,一个函数中如何使用一个变量的,就用一个字串表示出来了。
对字串的处理总是简单易懂的。
实际的情况要复杂得多。这样直接得到的特征串常常意义不大。还需要简化。
比如
2JL1E
就是“写-跳-标-读-End”就可以简化为 21E
再比如
ebx = 0
? = ebx
while (?)
{
ebx = ?
? = ebx
}
return
对变量ebx来说,特征串是 65LJ65JLE
容易理解这个特征串可以简化为 6565E
3.2 根据变量特征串进行变量优化
比如我们遇到了一个特征串是:
61E
即一个变量被赋值,使用了一次,就结束了。则一般情况下,可以把它“消”
掉,即直接使用赋值的源变量。
3.3 根据变量特征串进行变量分离
比如我们遇到了一个特征串是:
211211E
显然这可以分为两个变量。
分离变量是后面数据类型分析的基础。
比如一个寄存器ecx在一个函数中有多次使用,每次之间
并无联系,我们就不能认为它是一个变量,而应该分为ecx1,ecx2,ecx3等等。变量分离
以后,才好给它们加数据类型。
4. 标准C++头文件解析
已经基本支持一般的头文件,支持//注解,/**/注解,#define,typedef,函数预定义,
struct,enum,class等
5. 标准库函数解析
现在是读取vc的LIB文件,从中解析出一个个的标准函数,生成自己的标识文件。分析时
进行全比较。要注意重定位项。
6. 流程分析
char finger_for[] = "0_jmp1_from2_0_from1_0_jxx3_0_jmp2_from3_";
char finger_if [] = "0_jxx1_0_from1_";
char finger_if_else[] = "0_jxx1_0_jmp2_from1_0_from2_";
char finger_while[] = "from1_0_jxx2_0_jmp1_from2_";
char finger_dowhile[] = "from1_0_jxx1_";
7. 数据类型分析
表示数据类型的结构:
enum VarTT
{
vtt_unknown = 0,
vtt_base = 1, // 是基类型,不依赖于其它类型
vtt_signed, // 缺省为unsigned
vtt_typedef,
vtt_array, // 数组
vtt_point,
vtt_class, // 结构,或类
vtt_funcpoint,
vtt_const, // const
vtt_enum,
vtt_simple, // 如果不知道是什么类型,就用这个
//简单类型 对每一个size都会有一个简单类型,实际上未知类型。名叫bit8之类
};
struct SVarType
{
VarTypeID id; // 如果谁需要这个结构,只要记下这个id就行了
// 通过这个id与 SVar 联系
VarTT type;
union
{
struct
{
CFuncType* pFuncType; // for vtt_funcpoint, if this is a func pointer
} m_funcpoint; // = 1 means unknown funcpoint
struct
{
char classname[80]; //!!! only for unknown struc !
Class_st* pClass; // if this is a class
} m_class;
struct
{
VarTypeID id_pointto; // if this is a pointer
} m_point;
struct
{
SIZEOF arraynum; // if this is a array, this is item count
VarTypeID id_arrayitem; // if this is a array, this is item id
} m_array;
struct
{
VarTypeID id_base; // 这个数据类型的基类
PSTR name;
} m_typedef;
struct
{
VarTypeID id_base; // 这个数据类型的基类
} m_const;
struct
{
VarTypeID id_base; // 这个数据类型的基类
PSTR name;
} m_signed;
struct
{
SIZEOF opsize;
PSTR name;
} m_base; // 基类型
struct
{
SIZEOF opsize;
} m_simple;
struct
{
char enumname[80];
enum_st* m_penum;
} m_enum;
};
};
注意其中的vtt_simple。每一个变量初始化的时候,都根据它的sizeof给它一个vtt_simple
类型的数据类型。意思是它的数据类型为未知。如果在代码中遇到一个api call,则可以
根据我们事先从标准头文件中得到的该api的函数预定义,获得它的各参数数据类型,及
返回值数据类型。并由此类推,得到其它变量的数据类型。比如遇到一个
i_Assign B, A
如果已经B的数据类型是HWND,而A的数据类型是vtt_simple,则可以放心地把A的数据类型
也置为HWND.
再比如,
i_ReadPointto B,A
如果已经B的数据类型是char,而A的数据类型是vtt_simple,则可以放心地把A的数据类型
也置为char*.
typedef const char * PCSTR;
8. 未知指令的处理(未实现)
input edi
xor eax, eax
or ecx, -1
repnz scasb
not ecx
dec ecx
mov eax, ecx
----------
edi = ?
eax = 0
ecx = -1
??? repnz scasb
ecx = ?(ecx)
ecx = ecx -1
mov eax, ecx
-----------
edi = ?
eax = 0
ecx = -1
unknow(write ecx, edi read al, edi[])
ecx = ?(ecx)
ecx = ecx -1
mov eax, ecx
------
edi = ?
eax = 0
ecx = -1
unknow
{
read_write ecx
read_write edi
read edi[]
read al
}
ecx = ?(ecx)
ecx = ecx -1
mov eax, ecx
------
edi = ?
unknow
{
read_write edi
read edi[]
write ecx
write eax
}
mov eax, ecx
-----------
edi = ?
unknow
{
read edi
write ecx
}
mov eax, ecx
-----------
edi = ?
ecx = unknown_math(edi);
mov eax, ecx
-----------
9。实例:
=============================文章结束================================
PUSH -01
PUSH 00406CB8
MOV EAX,FS:[00000000]
PUSH EAX
MOV FS:[00000000],ESP
SUB ESP,+08
...
MOV DWORD PTR [ESP+10],00000000
...
MOV DWORD PTR [ESP+10],FFFFFFFF
...
MOV ECX,[ESP+08]
MOV FS:[00000000],ECX
ADD ESP,+14
RET
-----------------------
PUSH -01
PUSH 00406CB8
MOV EAX,FS:[00000000]
PUSH EAX
MOV FS:[00000000],ESP
...
MOV DWORD PTR [ESP+8],00000000
...
MOV DWORD PTR [ESP+8],FFFFFFFF
...
MOV ECX,[ESP]
MOV FS:[00000000],ECX
ADD ESP,+0c
RET
_EH_prolog
CxxThrowException
call _EH_prolog
的意思是 mov ebp, esp
_CxxThrowException 是 __stdcall 8
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -