|
《基于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;
- }
[color=rgb(81, 112, 58) !important]复制代码
___________________________________________________________________________________________________
上面是一个模拟简单的程序注册流程,判断用户输入
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,[local.17] ; 设置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:[edi] ; 循环赋值
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,[local.1] ; 变量地址放入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 [local.1],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 ; 返回 ret add 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:[esp+4] ; 获取变量指针到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:[esp+C] ; 获取输入数到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 ; 返回 ret add 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 ; 返回 ret add 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;
- }
[color=rgb(81, 112, 58) !important]复制代码
___________________________________________________________________________________________________
上面通过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,[local.18] ; 设置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:[edi] ; 循环复制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,[local.1] ; 取变量地址放入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,[local.1] ; 赋值变量值到ecx
0040F9B9 |. 894D F8 mov [local.2],ecx ; ecx放入局部变量中
0040F9BC |. 837D F8 00 cmp [local.2],0x0 ; 判断是否为0
0040F9C0 |. 74 0E je short Test3.0040F9D0 ; 等于0就跳转,执行相应内容
0040F9C2 |. 837D F8 01 cmp [local.2],0x1 ; 判断是否为1
0040F9C6 |. 74 17 je short Test3.0040F9DF ; 等于1就跳转,执行相应内容
0040F9C8 |. 837D F8 02 cmp [local.2],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:[esp+0x4] ; 取变量指针到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:[esp+0xC] ; 输入数值到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;
- }
[color=rgb(81, 112, 58) !important]复制代码
__________________________________________________________________________________________________
上述程序通过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,[local.18] ; 设置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:[edi] ; 循环复制CC
0040F998 |. C745 F8 00000>mov [local.2],0x0 ; 赋值变量y
0040F99F |. C745 FC 01000>mov [local.1],0x1 ; 赋值变量x
0040F9A6 |. EB 09 jmp short Test3.0040F9B1 ; 跳转到for循环判断
0040F9A8 |> 8B45 FC /mov eax,[local.1] ; x的值放入eax
0040F9AB |. 83C0 01 |add eax,0x1 ; eax加1
0040F9AE |. 8945 FC |mov [local.1],eax ; 自加1的值放入变量x
0040F9B1 |> 837D FC 64 cmp [local.1],0x64 ; 判断x值是否为100
0040F9B5 |. 7F 0B |jg short Test3.0040F9C2 ; 相等就跳转
0040F9B7 |. 8B4D FC |mov ecx,[local.1] ; 变量x值放入ecx
0040F9BA |. 034D F8 |add ecx,[local.2] ; x+y的值放入ecx
0040F9BD |. 894D F8 |mov [local.2],ecx ; 把ecx的值放入变量y
0040F9C0 |.^ EB E6 \jmp short Test3.0040F9A8 ; 跳转到判断处
0040F9C2 |> 8B55 F8 mov edx,[local.2] ; 最终变量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语句 |
|
|
|