admin 发表于 2015-11-14 18:34:40

【转】《基于VC平台下C++反汇编与逆向分析研究——No.2》

————————————————————————————————————————————————
分析环境:WIN7sp1
所用工具:VC++6.0/OllyDBG/IDA
适用人群:有一定计算机基础,熟悉C/C++编程,熟悉X86系列汇编/了解OD/IDA等调试工具使用,对逆向安全有极大兴趣者!
————————————————————————————————————————————————
开篇前言:
      数据类型和运算符是任何编程语言的基础,而对于逆向而言亦是,只有牢固的基础才能走得更远...


————————————————————————————————————————————————
正文部分:
       本节主要从汇编层面来全面解析基本的数据类型和运算符,如下:
___________________________________________________________________________________________________
本帖隐藏的内容
[*]#include<stdio.h>
[*]int main()
[*]{
[*]      int a,b,c,s;
[*]      scanf("%d%d%d",&a,&b,&c);
[*]      s=a+b+c;
[*]      printf("%d",s);
[*]      return 0;
[*]}
[*]

复制代码

_______________________________________________________________________________________________________
       上面的源码是一个简单的三角形求周边的程序,好了,直接复制到VC++6.0中选择Debug方式编译生成,直接载入OD,来到main函数:
Debug版本:
00401010 >|> \55            push ebp                                 ;保存ebp
00401011|.8BEC          mov ebp,esp                              ;保存esp到ebp
00401013|.83EC 50       sub esp,0x50                           ;开辟局部变量空间
00401016|.53            push ebx                                 ;入栈ebx
00401017|.56            push esi                                 ;入栈esi
00401018|.57            push edi                                 ;入栈edi
00401019|.8D7D B0       lea edi,                     ;设置初始化内存地址
0040101C|.B9 14000000   mov ecx,0x14                           ;设置循环次数
00401021|.B8 CCCCCCCC   mov eax,0xCCCCCCCC                     ;赋值eax int 3中断
00401026|.F3:AB         rep stos dword ptr es:            ;循环复制int 3
00401028|.8D45 F4       lea eax,                        ;变量c的内存地址放到eax
0040102B|.50            push eax                                 ;入栈c的地址
0040102C|.8D4D F8       lea ecx,                        ;变量b地址放到eax
0040102F|.51            push ecx                                 ;入栈b的地址
00401030|.8D55 FC       lea edx,                        ;变量a地址放到eax
00401033|.52            push edx                                 ;入栈a的地址
00401034|.68 20504200   push Test2.00425020                      ; /format = "%d%d%d"
00401039|.E8 D2000000   call Test2.scanf                         ; \scanf
0040103E|.83C4 10       add esp,0x10                           ;恢复esp的值4个push
00401041|.8B45 FC       mov eax,                        ;变量a的值放入eax
00401044|.0345 F8       add eax,                        ;变量b的值加上a的和放入eax中
00401047|.0345 F4       add eax,                        ;变量c的值加上ab的和放入eax
0040104A|.8945 F0       mov ,eax                        ;把abc的和放入变量s中
0040104D|.8B4D F0       mov ecx,                        ;变量s的值放入ecx
00401050|.51            push ecx                                    ; /<%d>
00401051|.68 1C504200   push Test2.0042501C                      ; |format = "%d"
00401056|.E8 35000000   call Test2.printf                        ; \printf
0040105B|.83C4 08       add esp,0x8                              ;恢复esp,2个push
0040105E|.33C0          xor eax,eax                              ;eax清零
00401060|.5F            pop edi                                  ;恢复寄存器edi
00401061|.5E            pop esi                                  ;恢复寄存器esi
00401062|.5B            pop ebx                                  ;恢复寄存器ebx
00401063|.83C4 50       add esp,0x50                           ;恢复局部变量空间
00401066|.3BEC          cmp ebp,esp                              ;测试esp值是否与之前一致
00401068|.E8 03010000   call Test2._chkesp                     ;如esp值不一致,调用调试信息
0040106D|.8BE5          mov esp,ebp                              ;恢复esp值
0040106F|.5D            pop ebp                                  ;恢复ebp
00401070\.C3            retn                                     ;返回 等于ret加上add esp,4

       和之前的Hello World大同小异,相信大家看起来都没问题,我希望大家能记熟这种框架模式,对以后逆向的深入有很大用处,我们再来看看 Release版本的:
Release版本:
00401000/$83EC 0C       sub esp,0xC                              ;开辟局部变量空间
00401003|.8D4424 08   lea eax,dword ptr ss:         ;把变量c的地址放入eax
00401007|.8D4C24 04   lea ecx,dword ptr ss:         ;把变量b的地址放入ecx
0040100B|.8D5424 00   lea edx,dword ptr ss:               ;把变量a的地址放入edx
0040100F|.50            push eax                                 ;入栈c的地址
00401010|.51            push ecx                                 ;入栈b的地址
00401011|.52            push edx                                 ;入栈a的地址
00401012|.68 34804000   push Test2.00408034                      ;ASCII "%d%d%d"
00401017|.E8 55000000   call Test2.00401071                      ;调用scanf函数
0040101C|.8B4424 10   mov eax,dword ptr ss:          ;把a的值放入eax
00401020|.8B4C24 14   mov ecx,dword ptr ss:          ;把b的值放入ecx
00401024|.03C1          add eax,ecx                              ;a+b的和放入eax中
00401026|.8B4C24 18   mov ecx,dword ptr ss:          ;把c的值放入ecx
0040102A|.03C1          add eax,ecx                              ;c的值和ab的和放入eax中
0040102C|.50            push eax                                 ;入栈三数之和
0040102D|.68 30804000   push Test2.00408030                      ;ASCII "%d"
00401032|.E8 09000000   call Test2.00401040                      ;调用printf函数
00401037|.33C0          xor eax,eax                              ;eax清零
00401039|.83C4 24       add esp,0x24                             ;恢复esp   
0040103C\.C3            retn                                     ;返回等于retadd esp,4

            大家可能不理解的就是最后的add esp,0x24,OK,先看下入口处的sub esp,0xC,注意次数是十六进制,大家都知道一个push操作,eap就相当于sub esp,4 此处共6个push,所以这里的值等于6个push的值0x18加上sub esp,0xc 的0xC 正好是0x24,有疑问的大家可以试试在源码中多添加一个push操作来看看此处add esp的值!
_______________________________________________________________________________________________________
#include<stdio.h>
int main()
{
short int a=1;
int b=2;
long int c=3;
float d=1.222;
double e=1.333;
long double f=1.234;
char g='a';
char *h;
h=&g;
return 0;
}
_______________________________________________________________________________________________________

       上面这段简单的源码中,涉及了很多基本数据类型,如:int整型 ,float单精度浮点型,double双精度浮点型,char字符型,指针类型,本节重要点就是来分析这些基本数据类型在汇编中的表现形式,先看下Debug版本的:

Debug版本:
00401250 >/> \55            push ebp                                 ;保存ebp
00401251|.8BEC          mov ebp,esp                              ;保存esp到ebp
00401253|.83EC 68       sub esp,0x68                           ;开辟局部变量空间
00401256|.53            push ebx                                 ;入栈ebx
00401257|.56            push esi                                 ;入栈esi
00401258|.57            push edi                                 ;入栈edi
00401259|.8D7D 98       lea edi,                     ;设置初始化局部变量地址
0040125C|.B9 1A000000   mov ecx,0x1A                           ;设置循环次数
00401261|.B8 CCCCCCCC   mov eax,0xCCCCCCCC                     ;赋值eax int 3中断
00401266|.F3:AB         rep stos dword ptr es:            ;循环复制int3
00401268|.66:C745 FC 01>mov word ptr ss:,0x1            ;赋值1到局部变量short int类型a中,参数:word16位
0040126E|.C745 F8 02000>mov ,0x2                        ;赋值2到局部变量int类型b中,参数:dword32位
00401275|.C745 F4 03000>mov ,0x3                        ;赋值3到局部变量float类型c中,参数:dword32位
0040127C|.C745 F0 7F6A9>mov ,0x3F9C6A7F               ;赋值0x3F9DF3B6到局部变量float类型d中,参数:dword32位
00401283|.C745 E8 8716D>mov ,0xCED91687               ;存放低位
0040128A|.C745 EC F753F>mov ,0x3FF553F7               ;double类型e 存放高位参数:qword 64位
00401291|.C745 E0 5839B>mov ,0xC8B43958               ;存放低位
00401298|.C745 E4 76BEF>mov ,0x3FF3BE76               ;long double类型f 存放高位参数:qword 64位
0040129F|.C645 DC 61    mov byte ptr ss:,0x61          ;char类型g Ascll码的a对应0x61参数:byte 8位
004012A3|.8D45 DC       lea eax,                        ;获取字符变量地址到eax
004012A6|.8945 D8       mov ,eax                     ;把eax放入指针中h中
004012A9|.33C0          xor eax,eax                              ;eax清零
004012AB|.5F            pop edi                                  ;出栈edi
004012AC|.5E            pop esi                                  ;出栈esi
004012AD|.5B            pop ebx                                  ;出栈ebx
004012AE|.8BE5          mov esp,ebp                              ;恢复esp
004012B0 >|.5D            pop ebp                                  ;恢复ebp
004012B1\.C3            retn                                     ;返回 等于ret add esp,4

       分析前,我们先普及一下计算机微计量单位,位bit    即10101010   每个数为1位,字节byte 即大家看到的文件大小字节,一个字节等于8位,字word1个字等于2个字节即16位双字Dword1个双字等于2个字,即32位eax寄存器就是32位,4字qword同理就是64位!当然还有更大的,我们这里不做描述!
      short int 类型为16位故:mov word ptr ss:,0x1    int类型为32位,故:mov ,0x2    默认就是32位,double为64位,就会使用2个32位来存放,即一个存放高位,一个低位!例如这句:double e=1.333; 1.333在内存中是以IEEE编码来存储,1.333对应的IEEE是3FF553F7CED91687,故会出现这两句汇编指令:mov ,0xCED91687    mov ,0x3FF553F7,Ascll码的0x61对应的就是小写字母a,好了,看下Release版

Release版本:
00401000/$33C0          xor eax,eax         ;eax清零
00401002\.C3            retn         ;返回等于retadd esp,4
这些变量未参数实际使用,Release会自动优化掉!
_______________________________________________________________________________________________________


[*]#include < stdio.h >
[*]int main()
[*]{
[*]    int a = 2,
[*]    b = 4,
[*]    c;
[*]    c = a + b;
[*]    c = a * b;
[*]    c = a / b;
[*]    c = a % b;
[*]    c = a && b;
[*]    c = a || b;
[*]    c = !b;
[*]    c++;
[*]    c--;
[*]    return 0;
[*]}
[*]

复制代码

______________________________________________________________________________________________________

Debug版本:
00410680 >/> \55            push ebp                                 ;ebp入栈保存
00410681|.8BEC          mov ebp,esp                              ;保存esp
00410683|.83EC 54       sub esp,0x54                           ;开辟局部变量空间
00410686|.53            push ebx                                 ;入栈ebx
00410687|.56            push esi                                 ;入栈esi
00410688|.57            push edi                                 ;入栈edi
00410689|.8D7D AC       lea edi,                     ;设置初始化局部变量地址
0041068C|.B9 15000000   mov ecx,0x15                           ;循环次数
00410691|.B8 CCCCCCCC   mov eax,0xCCCCCCCC                     ;赋值eax int 中断
00410696|.F3:AB         rep stos dword ptr es:            ;循环复制int 3 中断
00410698|.C745 FC 05000>mov ,0x5                        ;赋值2到变量a
0041069F|.C745 F8 02000>mov ,0x2                        ;赋值4到变量b
004106A6|.8B45 FC       mov eax,                        ;赋值a的值到eax
004106A9|.0345 F8       add eax,                        ;b的值与a的值相加放入eax
004106AC|.8945 F4       mov ,eax                        ;ab的和放入变量c
004106AF|.8B4D FC       mov ecx,                        ;赋值a的值到ecx
004106B2|.0FAF4D F8   imul ecx,                     ;变量b与变量a的乘积放入ecx
004106B6|.894D F4       mov ,ecx                        ;ab的积值放入变量c
004106B9|.8B45 FC       mov eax,                        ;赋值a的值到eax
004106BC|.99            cdq                                    ;双字32位扩展到64位,eax的高位放入edx中
004106BD|.F77D F8       idiv                            ;除法运算   idiv无符号除法
004106C0|.8945 F4       mov ,eax                        ;ab的商放入变量c
004106C3|.8B45 FC       mov eax,                        ;赋值a的值到eax
004106C6|.99            cdq                                    ;双字32位扩展到64位,eax的高位放入edx中
004106C7|.F77D F8       idiv                            ;除法运算   idiv无符号除法
004106CA|.8955 F4       mov ,edx                        ;ab的余数放入变量c
004106CD|.837D FC 00    cmp ,0x0                        ;与运算,测试变量a是否为0,如为0就跳转
004106D1|.74 0F         je short Test2.004106E2                  ;a值不为0,未发生跳转
004106D3|.837D F8 00    cmp ,0x0                        ;与运算,测试变量b是否为0,如为0就跳转
004106D7|.74 09         je short Test2.004106E2                  ;b值不为0,未发生跳转
004106D9|.C745 F0 01000>mov ,0x1                        ;与运算为真
004106E0|.EB 07         jmp short Test2.004106E9               ;无条件跳转
004106E2|>C745 F0 00000>mov ,0x0
004106E9|>8B55 F0       mov edx,                        ;与运算值放入edx
004106EC|.8955 F4       mov ,edx                        ;ab的与运算值放入变量c
004106EF|.837D FC 00    cmp ,0x0                        ;或运算,测试变量a是否为0,如不等于0就跳转
004106F3|.75 0F         jnz short Test2.00410704               ;a值不为0,发生跳转
004106F5|.837D F8 00    cmp ,0x0
004106F9|.75 09         jnz short Test2.00410704
004106FB|.C745 EC 00000>mov ,0x0
00410702|.EB 07         jmp short Test2.0041070B
00410704|>C745 EC 01000>mov ,0x1                  ;ab或运算为真
0041070B|>8B45 EC       mov eax,                        ;或运算值放入edx
0041070E|.8945 F4       mov ,eax                        ;ab的或运算值放入变量c
00410711|.33C9          xor ecx,ecx                              ;ecx清零
00410713|.837D F8 00    cmp ,0x0                        ;求反运算,测试变量b是否为0
00410717|.0F94C1      sete cl                                  ;依据zf标志位的值设置cl的值
0041071A|.894D F4       mov ,ecx                        ;b逻辑取反的值放入变量c
0041071D|.8B55 F4       mov edx,                        ;变量c的值放入edx
00410720|.83C2 01       add edx,0x1                              ;edx自加1
00410723|.8955 F4       mov ,edx                        ;edx的值放入变量c
00410726|.8B45 F4       mov eax,                        ;变量c的值放入eax
00410729|.83E8 01       sub eax,0x1                              ;eax自减1
0041072C|.8945 F4       mov ,eax                        ;eax的值放入变量c
0041072F|.33C0          xor eax,eax                              ;eax清零
00410731|?5F            pop edi         ;恢复edi
00410732|.5E            pop esi                                  ;恢复esi
00410733|?5B            pop ebx         ;恢复ebx
00410734|?8BE5          mov esp,ebp         ;恢复esp的值
00410736|?5D            pop ebp         ;恢复ebp
00410737|?C3            retn         ;返回等于ret add esp,4
       上述每条汇编指令的注释我写的都很详细,大家可以对照源码来看,注意cdq指令 ,重点看下理解乘除法运算,以及逻辑运算!Release版本同上。

Release版本:
00401000/$33C0          xor eax,eax
00401002\.C3            retn

这些变量未参数实际使用,Release会自动优化掉!
_______________________________________________________________________________________________________

         本节中,肯定没有全面涉及数据类型和运算符,这里只做为一种分析的方法,各位私下多练习下这些基础,吃透吃熟,对以深入后尤关重要!至于逻辑判断,下节我会详细介绍。

页: [1]
查看完整版本: 【转】《基于VC平台下C++反汇编与逆向分析研究——No.2》