📄 me.txt
字号:
2005.2.5 4:20 新的思想:每一次的优化都要能看到见,比如变量优化
要把当前变量的使用树显示出来,然后告诉我这次优化要用什么策略,
为什么。
基本一个变量优化:
首先只考虑寄存器变量。
对整个函数过一遍,保留jmp和label和return,其它与本变量无关的语句去掉。
然后把空的jmp和label去掉。这时剩下的部分可以打印出来给用户看了。
堆栈变量是函数一开始就sub esp, nn,留出空间,在整个函数中每一个量有
固定的意义,一般不变。而,再遇到call,需要把参数一个个push入栈,这时
候的栈里的东西是临时的,显然不能看作堆栈变量。它不是永久的。说不定再
次使用这个空间时,会有另一层意思。所以函数结束后,要加一个虚拟语句,
指示前面所用的临时变量失效。
这些是有符号的。
case JMP_jl:
case JMP_jg:
case JMP_jle:
case JMP_jge:
判断变量有无符号不是exe2c的重点,这部分代码暂删
case JMP_jge:
-------------
要把类似IDA的分析过程和EXE2C的过程明确分开
-------------
2005.2.15 现在的问题:我对jmp和label的流程分析是在高级阶段做的。而要
把ASM按IDA输出,需要把它移到ASM一级去。
原则:在低端能做的事情,不要到高端去做
为了简化复杂逻辑的分析,我把cmp和jxx合为一条指令。但这又增加了流程分析的
难度。
规范的for应该先做条件判断。但for (int i = 0; i<10; i++)优化后可以把第
一次的判断去掉。这时要把它判断为for就比较困难。这时它会被判断为do-while
命令:
funcpara 4 4 4
表明当前函数有3个参数
函数是一个instrlist,每一个instr中包括它用到的变量var_w,var_r1,var_r2,
有一个函数(?),遍历instrlist,根据每一个instr中的var,生成变量表。这个
过程只进行一次。新生成的变量叫M_T。也就是说,一个M_T对应于多个instr中的
多个var。它们之间的关联显然应该是var中有一个指针,指向它对应的M_T。如果
M_T发生变化,比如删除或合并,则需要遍历instrlist同步这种变化。
对于push/pop产生的临时变量,可以在它pop后,给M_T_list发一条消息,说明某
些temvar不会再被引用。
加一个特征注解
//SuperC_func: 只在<>中使用
//SuperC_var: 只在<>中使用
遇到一个call,一般会形成如下形状:
i_Call
i_CallPara
i_CallRet
输出的时候,可以这么处理。遇到i_Call,往后找i_CallRet,如果找到了,就显示
eax = ?;然后i_CallRet就不需要显示了。
不认识这条指令:
0F AF C0 imul eax, eax
问题,对于一个21E,也就是“写--读--End”,优化方法当然是把这个变量变成
临时变量。但,这种优化总是进行吗?
数据类型,显然从函数定义中获得的数据类型是比较可靠的。但在给一个变量数据类型
前,我们需要“变量分离”。比如一个寄存器ecx在一个函数中有多次使用,每次之间
并无联系,我们就不能认为它是一个变量,而应该分为ecx1,ecx2,ecx3等等。变量分离
以后,才好给它们加数据类型。
IDA是成功的。它的分析模式简单,每一步都是可逆的。一段代码,用户可以U掉,还可
以D成数据,后来又发现它是代码,还可以C回来,如此多次也不会损失任何信息。exe2c
就麻烦多了。比如:
call func1
ret
如果认为当前函数没有返回值,写成
func1();
return;
后来用户又发现它是有返回值的,就很难改回来。还有函数的参数等,都有这个问题。
因为,exe2c的分析过程是不可逆的。
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
-----------
edi = ?
eax = 0
eax = unknown_math(edi, al);
所有的分析都可以分为两类,一类是只能运行一遍的,一类是可以重复运行的。
可以用两个函数指针数组。第一个叫tblProgressAna,意思是进度分析。分析
进行到哪一步,要记下来,叫m_nStep。如果全部都通过了,就置m_nStep=100.
可以进行下一系列的分析了。
第二个叫tblRepeatAna,可以循环调用的。如果一个函数return true,就反复
调用它,直到它return false才调用下一个。直到所有表中的函数都调用完了,
OK,分析结束。
=============================文章开始================================
2005.4.8
序,意义
提高公司知名度。
吸引人才,获得政府支持,吸引客户。
序,可行性
现DLL有18k行代码。
1.反汇编
把机器码反汇编为可读的汇编指令,把90h变为nop,这个过程叫反汇编。反汇编
的过程用得很多,可以在很多debugger,分析器中找到。容易理解,exetoc的第
一步,肯定也是反汇编。这是所有后续分析的基础。
但是,exetoc的反汇编与通常意义上的反汇编是不同的,它不仅仅是把机器码转
化为汇编指令就可以了,它要求更多,它要区分是add还是sub,有几个操作数。
简单地讲,传统的反汇编的输出结果是一行字串,而exetoc的要求,输出结果是
一个类似如下的结构:
enum OP_TYPE
{
OP_Invalid = 0,
OP_Address = 1,
OP_Register = 2,
OP_Segment = 3,
OP_Immed = 4,
};
typedef struct OPERITEM
{
OP_TYPE mode; //OP_Register, ...
BYTE rwflag; //0:Unknown 1:Read 2:Write 3:Access
BYTE opersize; //1:BYTE, 2:WORD, 4:DWORD, 8:double DWORD
union
{
struct
{
BYTE seg_index; //SegReg Index!!!
//BYTE reg_size; //2:WORD 4:DWORD
BYTE base_reg_index;
BYTE off_reg_index;
BYTE off_reg_scale;
DWORD off_value;
} addr; //for Mode 1:Address
struct
{
DWORD reg_index;
} reg; //for Mode 2:Register
struct
{
DWORD sreg_index;
} sreg; //for Mode 3:Segment Register
struct
{
DWORD immed_value;
} immed; //for Mode 4:Immed
};
} *POPERITEM;
enum OPCODETYPE
{
C_ADD,
C_OR,
C_ADC,
C_SBB,
C_AND,
C_SUB,
C_XOR,
C_CMP,
...,
}
typedef struct XCPUCODE
{
OPCODETYPE opcode; // C_MOV...
BYTE lockflag; // for LOCK prefix
BYTE repeatflag; // for REPZ/REPNZ prefix
OPERITEM op[3];
} *PXCPUCODE;
这个XCPUCODE结构实际上是每一条指令的一种结构表达形式,这种结构表达方式
才可以被后续的分析使用。
2.内部伪码
以上的XCPUCODE结构与Intel的X86指令集是一一结应关系,即每一条X86指令,都
可以有一个对应的XCPUCODE结构来表达。但是,这种结构也是不方便进行分析的。
比如一条指令
mov eax, [ebx+ecx*4+1000]
对应的结构太复杂,不方便分析,有必要创造一种中间的伪码来方便后续的分析。
这种伪码要很简单,读写关系明确。如下结构:
enum HLType
{
i_Jump = 0x101,
i_Label = 0x102,
i_Begin ,
i_End ,
i_Assign,
i_Var ,
i_Add, i_Sub, i_Xor, i_Sar, i_And, i_Shl, i_Shr, i_Imul,
i_Readpointto,
i_Writepointto,
i_GetAddr,
i_Call,
i_CallApi,
i_CallPara,
i_CallRet,
i_CplxBegin,
i_CplxEnd,
i_Nop,
...,
};
class INSTR
{
public:
HLType type; // i_Assign ...
union
{
struct
{
JxxType jmp_type; //JMP_???
DWORD jmpto_off;
PINSTR the_label;
PINSTR next_ref_of_this_label; //这里组成个链,用来保存对一个label的所有ref
} jmp; //for type = i_Jump only
struct
{
PINSTR ref_instr; //for type = i_Label only
ea_t label_off;
bool f_conti;
} label;
struct
{
CFunc* call_func; // for i_Call
CApi* papi; // for i_CallApi
signed int esp_level;
PINSTR p_callpara;
PINSTR p_callret;
} call;
VAR var_w;
VAR var_r1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -