admin 发表于 2015-11-20 21:08:53

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


《基于VC平台下C++反汇编与逆向分析研究——No.3》
伤情最是晚凉天,憔悴斯人不堪怜。邀酒摧肠三杯醉,寻香惊梦五更寒。
钗头凤斜卿有泪,荼蘼花了我无缘。小楼寂寞心宇月,也难如钩也难圆。
————————————————————————————————————————————————
分析环境:WIN7sp1
所用工具:VC++6.0/OllyDBG/IDA
适用人群:有一定计算机基础,熟悉C/C++编程,熟悉X86系列汇编/了解OD/IDA等调试工具使用,对逆向安全有极大兴趣者!
————————————————————————————————————————————————
开篇前言:
      数据类型和运算符是任何编程语言的基础,而对于逆向而言亦是,只有牢固的基础才能走得更远...

本帖隐藏的内容————————————————————————————————————————————————
正文部分:
       本节主要从汇编层面来全面解析程序的基本流程控制语句:if -else,switch,for 如下:
___________________________________________________________________________________________________

源码:

[*]#include<stdio.h>
[*]int main()
[*]{
[*]      int x;
[*]      printf("请输入密码");
[*]      scanf("%d",&x);
[*]      if(x==123456)
[*]                printf("成功");
[*]      else
[*]                printf("失败");
[*]      return 0;
[*]}

复制代码

___________________________________________________________________________________________________
上面是一个模拟简单的程序注册流程,判断用户输入


Debug版本:
00401010 >|> \55            push ebp                                 ;ebp入栈保存
00401011|.8BEC          mov ebp,esp                              ;esp保存到ebp中
00401013|.83EC 44       sub esp,0x44                           ;开辟局部变量空间
00401016|.53            push ebx                                 ;入栈ebx
00401017|.56            push esi                                 ;入栈esi
00401018|.57            push edi                                 ;入栈edi
00401019|.8D7D BC       lea edi,                     ;设置CC操作起始地址
0040101C|.B9 11000000   mov ecx,0x11                           ;设置循环次数
00401021|.B8 CCCCCCCC   mov eax,0xCCCCCCCC                     ;赋值eax CC,int 3
00401026|.F3:AB         rep stos dword ptr es:            ;循环赋值
00401028|.68 10604200   push Test3.00426010                      ; /format = "请输入密码"
0040102D|.E8 6E000000   call Test3.printf                        ; \printf
00401032|.83C4 04       add esp,0x4                              ;恢复esp
00401035|.8D45 FC       lea eax,                        ;变量地址放入eax
00401038|.50            push eax                                 ;参数入栈
00401039|.68 1C504200   push Test3.0042501C                      ; /format = "%d"
0040103E|.E8 DD000000   call Test3.scanf                         ; \scanf
00401043|.83C4 08       add esp,0x8                              ;恢复esp
00401046|.817D FC 40E20>cmp ,0x1E240                  ;判断是否为123456
0040104D|.75 0F         jnz short Test3.0040105E               ;不等于就跳转
0040104F|.68 20504200   push Test3.00425020                      ; /format = "成功"
00401054|.E8 47000000   call Test3.printf                        ; \printf
00401059|.83C4 04       add esp,0x4                              ;恢复esp
0040105C|.EB 0D         jmp short Test3.0040106B               ;跳出循环
0040105E|>68 08604200   push Test3.00426008                      ; /format = "失败"
00401063|.E8 38000000   call Test3.printf                        ; \printf
00401068|.83C4 04       add esp,0x4                              ;恢复esp
0040106B|>33C0          xor eax,eax                              ;eax清零
0040106D|.5F            pop edi                                  ;恢复edi
0040106E|.5E            pop esi                                  ;恢复esi
0040106F|.5B            pop ebx                                  ;恢复ebx
00401070|.83C4 44       add esp,0x44                           ;恢复局部变量空间
00401073|.3BEC          cmp ebp,esp                              ;判断堆栈平衡
00401075|.E8 06010000   call Test3._chkesp                     ;调用调试信息
0040107A|.8BE5          mov esp,ebp                              ;恢复esp
0040107C|.5D            pop ebp                                  ;恢复ebp
0040107D\.C3            retn                                     ;返回retadd esp,4

if-else的基本框架:
cmp xx1,xx2   ;测试比较指令,如test
jnz;条件转移指令,如jle,jne等
........... ;if 语句内容
jmp   ;跳出判断
........... ;else语句内容

Release版本:
00401000/$51            push ecx                                 ;ecx入栈保存
00401001|.68 44804000   push Test3.00408044                      ;请输入密码
00401006|.E8 5C000000   call Test3.00401067                      ;printf函数
0040100B|.8D4424 04   lea eax,dword ptr ss:             ;获取变量指针到eax
0040100F|.50            push eax                                 ;参数
00401010|.68 40804000   push Test3.00408040                      ;%d
00401015|.E8 36000000   call Test3.00401050                      ;scanf函数
0040101A|.8B4424 0C   mov eax,dword ptr ss:             ;获取输入数到eax
0040101E|.83C4 0C       add esp,0C                               ;恢复esp
00401021|.3D 40E20100   cmp eax,1E240                            ;比较是否相等
00401026|.75 11         jnz short Test3.00401039               ;不相等就跳转
00401028|.68 38804000   push Test3.00408038                      ;成功
0040102D|.E8 35000000   call Test3.00401067                      ;printf函数
00401032|.83C4 04       add esp,4                              ;恢复esp
00401035|.33C0          xor eax,eax                              ;eax清零
00401037|.59            pop ecx                                  ;恢复ecx
00401038|.C3            retn                                     ;返回 retadd esp,4
00401039|>68 30804000   push Test3.00408030                      ;失败
0040103E|.E8 24000000   call Test3.00401067                      ;printf函数
00401043|.83C4 04       add esp,4                              ;恢复esp
00401046|.33C0          xor eax,eax                              ;eax清零
00401048|.59            pop ecx                                  ;恢复ecx
00401049\.C3            retn                                     ;返回 retadd esp,4


if-else的基本框架:

cmp xx1,xx2   ;测试比较指令,如test
jnz;条件转移指令,如jle,jne等
........... ;if 语句内容
retn
........... ;else语句内容
retn
___________________________________________________________________________________________________


[*]#include<stdio.h>
[*]int main()
[*]{
[*]      int x;
[*]      printf("请输入一个数字");
[*]      scanf("%d",&x);
[*]      switch(x)
[*]      {
[*]      case 0:
[*]               printf("你输入的数字是0");
[*]               break;
[*]      case 1:
[*]               printf("你输入的数字是1");
[*]               break;
[*]      case 2:
[*]               printf("你输入的数字是2");
[*]               break;
[*]      default:
[*]               printf("你输入的数字大于2");
[*]      }
[*]      return 0;
[*]}

复制代码

___________________________________________________________________________________________________

上面通过switch语句,来判断用户输入,有默认执行部分!

Debug版本:
0040F980 >/> \55            push ebp                                 ;ebp入栈保存
0040F981|.8BEC          mov ebp,esp                              ;esp保存到ebp中
0040F983|.83EC 48       sub esp,0x48                           ;开辟局部变量空间
0040F986|.53            push ebx                                 ;ebx入栈保存
0040F987|.56            push esi                                 ;esi入栈保存
0040F988|.57            push edi                                 ;edi入栈保存
0040F989|.8D7D B8       lea edi,                     ;设置CC初始化起始地址
0040F98C|.B9 12000000   mov ecx,0x12                           ;设置循环次数
0040F991|.B8 CCCCCCCC   mov eax,0xCCCCCCCC                     ;赋值eax CC,int 3
0040F996|.F3:AB         rep stos dword ptr es:            ;循环复制cc
0040F998|.68 4C604200   push Test3.0042604C                      ; /format = "请输入一个数字"
0040F99D|.E8 FE16FFFF   call Test3.printf                        ; \printf
0040F9A2|.83C4 04       add esp,0x4                              ;恢复esp
0040F9A5|.8D45 FC       lea eax,                        ;取变量地址放入eax
0040F9A8|.50            push eax                                 ;参数入栈
0040F9A9|.68 1C504200   push Test3.0042501C                      ; /format = "%d"
0040F9AE|.E8 6D17FFFF   call Test3.scanf                         ; \scanf
0040F9B3|.83C4 08       add esp,0x8                              ;恢复esp
0040F9B6|.8B4D FC       mov ecx,                        ;赋值变量值到ecx
0040F9B9|.894D F8       mov ,ecx                        ;ecx放入局部变量中
0040F9BC|.837D F8 00    cmp ,0x0                        ;判断是否为0
0040F9C0|.74 0E         je short Test3.0040F9D0                  ;等于0就跳转,执行相应内容
0040F9C2|.837D F8 01    cmp ,0x1                        ;判断是否为1
0040F9C6|.74 17         je short Test3.0040F9DF                  ;等于1就跳转,执行相应内容
0040F9C8|.837D F8 02    cmp ,0x2                        ;判断是否为2
0040F9CC|.74 20         je short Test3.0040F9EE                  ;等于2就跳转,执行相应内容
0040F9CE|.EB 2D         jmp short Test3.0040F9FD               ;条件均不成立时,跳到默认执行处
0040F9D0|>68 3C604200   push Test3.0042603C                      ; /format = "你输入的数字是0"
0040F9D5|.E8 C616FFFF   call Test3.printf                        ; \printf
0040F9DA|.83C4 04       add esp,0x4                              ;恢复esp
0040F9DD|.EB 2B         jmp short Test3.0040FA0A               ;跳出switch语句
0040F9DF|>68 2C604200   push Test3.0042602C                      ; /format = "你输入的数字是1"
0040F9E4|.E8 B716FFFF   call Test3.printf                        ; \printf
0040F9E9|.83C4 04       add esp,0x4                              ;恢复esp
0040F9EC|.EB 1C         jmp short Test3.0040FA0A               ;跳出switch语句
0040F9EE|>68 1C604200   push Test3.0042601C                      ; /format = "你输入的数字是2"
0040F9F3|.E8 A816FFFF   call Test3.printf                        ; \printf
0040F9F8|.83C4 04       add esp,0x4                              ;恢复esp
0040F9FB|.EB 0D         jmp short Test3.0040FA0A               ;跳出switch语句
0040F9FD|>68 08604200   push Test3.00426008                      ; /format = "你输入的数字大于2"
0040FA02|.E8 9916FFFF   call Test3.printf                        ; \printf
0040FA07|.83C4 04       add esp,0x4                              ;恢复esp
0040FA0A|>33C0          xor eax,eax                              ;eax清零
0040FA0C|.5F            pop edi                                  ;恢复edi
0040FA0D|.5E            pop esi                                  ;恢复esi
0040FA0E|.5B            pop ebx                                  ;恢复ebx
0040FA0F|.83C4 48       add esp,0x48                           ;恢复局部变量空间
0040FA12|.3BEC          cmp ebp,esp                              ;堆栈平衡判断
0040FA14|.E8 6717FFFF   call Test3._chkesp                     ;调用调试信息
0040FA19|.8BE5          mov esp,ebp                              ;恢复esp
0040FA1B|.5D            pop ebp                                  ;恢复ebp
0040FA1C\.C3            retn                                     ;返回 ret add esp,4


switch语句基本框架:

cmp xx0,xx1       ;可以是test等
je;跳转执行体1;此处也可以是其他条件转移指令
cmp xx0,xx2
je ;跳转执行体2
0,xx3
je cmp xx;跳转执行体3
jmp ;跳转默认执行体处
...... ;执行体1
jmp
...... ;执行体2
jmp
...... ;执行体3
jmp
...... ;默认执行体

Release版本:
00401000/$51            push ecx                                 ;ecx入栈
00401001|.68 78804000   push Test3.00408078                      ;请输入一个数字
00401006|.E8 7C000000   call Test3.00401087                      ;printf函数
0040100B|.8D4424 04   lea eax,dword ptr ss:         ;取变量指针到eax
0040100F|.50            push eax                                 ;参数
00401010|.68 74804000   push Test3.00408074                      ;%d
00401015|.E8 56000000   call Test3.00401070                      ;scanf函数
0040101A|.8B4424 0C   mov eax,dword ptr ss:         ;输入数值到eax
0040101E|.83C4 0C       add esp,0xC                              ;恢复esp
00401021|.83E8 00       sub eax,0x0                              ;判断; Switch (cases 0..2)
00401024|.74 39         je short Test3.0040105F                  ;如为0就跳转
00401026|.48            dec eax                                  ;判断
00401027|.74 25         je short Test3.0040104E                  ;如为1就跳转
00401029|.48            dec eax                                  ;判断
0040102A|.74 11         je short Test3.0040103D                  ;如为2就跳转
0040102C|.68 60804000   push Test3.00408060                      ;你输入的数字大于2; Default case of switch 00401021
00401031|.E8 51000000   call Test3.00401087                      ;printf函数
00401036|.83C4 04       add esp,0x4                              ;恢复esp
00401039|.33C0          xor eax,eax                              ;eax清零
0040103B|.59            pop ecx                                  ;恢复ecx
0040103C|.C3            retn                                     ;返回
0040103D|>68 50804000   push Test3.00408050                      ;你输入的数字是2; Case 2 of switch 00401021
00401042|.E8 40000000   call Test3.00401087                      ;printf函数
00401047|.83C4 04       add esp,0x4                              ;恢复esp
0040104A|.33C0          xor eax,eax                              ;eax清零
0040104C|.59            pop ecx                                  ;恢复ecx
0040104D|.C3            retn                                     ;返回
0040104E|>68 40804000   push Test3.00408040                      ;你输入的数字是1; Case 1 of switch 00401021
00401053|.E8 2F000000   call Test3.00401087                      ;printf函数
00401058|.83C4 04       add esp,0x4                              ;恢复esp
0040105B|.33C0          xor eax,eax                              ;eax清零
0040105D|.59            pop ecx                                  ;恢复ecx
0040105E|.C3            retn                                     ;返回
0040105F|>68 30804000   push Test3.00408030                      ;你输入的数字是0; Case 0 of switch 00401021
00401064|.E8 1E000000   call Test3.00401087                      ;printf函数
00401069|.83C4 04       add esp,0x4                              ;恢复esp
0040106C|.33C0          xor eax,eax                              ;eax清零
0040106E|.59            pop ecx                                  ;恢复ecx
0040106F\.C3            retn                                     ;返回


switch语句基本框架:

cmp xx0,xx1       ;可以是test等
je;跳转执行体1;此处也可以是其他条件转移指令
cmp xx0,xx2
je ;跳转执行体2
cmp xx0,xx3
je ;跳转执行体3
...... ;默认执行体
retn
...... ;执行体1
retn
...... ;执行体2
retn
...... ;执行体3
retn
___________________________________________________________________________________________________


[*]#include<stdio.h>
[*]int main()
[*]{
[*]      int x,y=0;
[*]      for(x=1;x<=100;x++)
[*]      {
[*]                y=x+y;
[*]      }
[*]      printf("%d",y);
[*]      return 0;
[*]}
[*]

复制代码

__________________________________________________________________________________________________
上述程序通过for循环来计算1+2+3...+100的值
Debug版本:
0040F980 >/> \55            push ebp                                 ;ebp入栈保存
0040F981|.8BEC          mov ebp,esp                              ;保存esp到ebp
0040F983|.83EC 48       sub esp,0x48                           ;开辟局部变量空间
0040F986|.53            push ebx                                 ;ebx入栈保存
0040F987|.56            push esi                                 ;esi入栈保存
0040F988|.57            push edi                                 ;edi入栈保存
0040F989|.8D7D B8       lea edi,                     ;设置CC操作起始地址
0040F98C|.B9 12000000   mov ecx,0x12                           ;设置循环地址
0040F991|.B8 CCCCCCCC   mov eax,0xCCCCCCCC                     ;赋值eax CCint 3
0040F996|.F3:AB         rep stos dword ptr es:            ;循环复制CC
0040F998|.C745 F8 00000>mov ,0x0                        ;赋值变量y
0040F99F|.C745 FC 01000>mov ,0x1                        ;赋值变量x
0040F9A6|.EB 09         jmp short Test3.0040F9B1               ;跳转到for循环判断
0040F9A8|>8B45 FC       /mov eax,                     ;x的值放入eax
0040F9AB|.83C0 01       |add eax,0x1                           ;eax加1
0040F9AE|.8945 FC       |mov ,eax                     ;自加1的值放入变量x
0040F9B1|>837D FC 64   cmp ,0x64                      ;判断x值是否为100
0040F9B5|.7F 0B         |jg short Test3.0040F9C2               ;相等就跳转
0040F9B7|.8B4D FC       |mov ecx,                     ;变量x值放入ecx
0040F9BA|.034D F8       |add ecx,                     ;x+y的值放入ecx
0040F9BD|.894D F8       |mov ,ecx                     ;把ecx的值放入变量y
0040F9C0|.^ EB E6         \jmp short Test3.0040F9A8                ;跳转到判断处
0040F9C2|>8B55 F8       mov edx,                        ;最终变量y的值放入edx
0040F9C5|.52            push edx                                 ; /<%d>
0040F9C6|.68 08604200   push Test3.00426008                      ; |format = "%d"
0040F9CB|.E8 D016FFFF   call Test3.printf                        ; \printf
0040F9D0|.83C4 08       add esp,0x8                              ;恢复esp
0040F9D3|.33C0          xor eax,eax                              ;eax清零
0040F9D5|.5F            pop edi                                  ;恢复edi
0040F9D6|.5E            pop esi                                  ;恢复esi
0040F9D7|.5B            pop ebx                                  ;恢复ebx
0040F9D8|.83C4 48       add esp,0x48                           ;恢复局部变量空间
0040F9DB|.3BEC          cmp ebp,esp                              ;测试堆栈平衡
0040F9DD|.E8 9E17FFFF   call Test3._chkesp                     ;调用调试信息
0040F9E2|.8BE5          mov esp,ebp                              ;恢复esp
0040F9E4|.5D            pop ebp                                  ;恢复ebp
0040F9E5\.C3            retn                                     ;返回ret   add esp,4

for基本循环框架:
jmp;无条件跳转到条件判断语句
.... ;for循环条件
cmp;判断是否满足条件 ;可以是其他条件测试指令
jg ;如果不满足就跳出for循环;可以是其他条件转移指令
.... ;for循环执行体
jmp ;无条件跳转到for循环条件

Release版本:
00401000/$33C9          xor ecx,ecx                              ;ecx清零,作为计数器
00401002|.B8 01000000   mov eax,0x1                              ;eax初始化为为1
00401007|>03C8          /add ecx,eax                           ;eax加上ecx的值放入ecx中
00401009|.40            |inc eax                                 ;eax自加1
0040100A|.83F8 64       |cmp eax,0x64                            ;判断是否为100
0040100D|.^ 7E F8         \jle short Test3.00401007                ;如不为100就跳转
0040100F|.51            push ecx                                 ;参数入栈
00401010|.68 30704000   push Test3.00407030                      ;ASCII "%d"
00401015|.E8 06000000   call Test3.00401020                      ;printf函数
0040101A|.83C4 08       add esp,0x8                              ;恢复esp
0040101D|.33C0          xor eax,eax                              ;eax清零
0040101F\.C3            retn                                     ;返回

for基本循环框架:

......;初始化各个参数
jle ;判断是否满足条件
......;跳出循环
___________________________________________________________________________________________________

本节主要对程序的流程化控制做逆向分析,此节对以后无论是逆向还是病毒分析都极其重要,下半节更新,while,do-while,goto语句



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