【转】《基于VC平台下C++反汇编与逆向分析研究——No.4》
————————————————————————————————————————————————分析环境:WIN7sp1
所用工具:VC++6.0/OllyDBG/IDA
适用人群:有一定计算机基础,熟悉C/C++编程,熟悉X86系列汇编/了解OD/IDA等调试工具使用,对逆向安全有极大兴趣者!
————————————————————————————————————————————————
开篇前言:
数据类型和运算符是任何编程语言的基础,而对于逆向而言亦是,只有牢固的基础才能走得更远...
————————————————————————————————————————————————
正文部分:
本节主要从汇编层面来全面解析程序的基本流程控制语句:while,do-while,goto 如下:
___________________________________________________________________________________________________
本帖隐藏的内容
[*]#include < stdio.h >
[*]int main()
[*]{
[*] int x = 1,
[*] y = 0;
[*] while (x <= 100)
[*] {
[*] y = x + y;
[*] x++;
[*] }
[*] printf("%d", y);
[*] return 0;
[*]}
复制代码
___________________________________________________________________________________________________
上面是一个使用while语句来计算1+2+3...+100的和的程序,使用了一个计数器!
Debug版本:
00401010 >|> \55 push ebp ;ebp入栈保存
00401011|.8BEC mov ebp,esp ;esp保存到ebp
00401013|.83EC 48 sub esp,0x48 ;开辟局部变量空间
00401016|.53 push ebx ;ebx入栈保存
00401017|.56 push esi ;esi入栈保存
00401018|.57 push edi ;edi入栈保存
00401019|.8D7D B8 lea edi, ;设置CC操作起始化地址
0040101C|.B9 12000000 mov ecx,0x12 ;设置循环次数
00401021|.B8 CCCCCCCC mov eax,0xCCCCCCCC ;赋值eax CC int 3
00401026|.F3:AB rep stos dword ptr es: ;循环复制cc
00401028|.C745 FC 01000>mov ,0x1 ;赋值局部变量x
0040102F|.C745 F8 00000>mov ,0x0 ;赋值局部变量y
00401036|>837D FC 64 /cmp ,0x64 ;测试x是否为100
0040103A|.7F 14 |jg short Test3.00401050 ;如果为100就跳出while循环
0040103C|.8B45 FC |mov eax, ;变量x值放入eax
0040103F|.0345 F8 |add eax, ;x+y的值放入eax
00401042|.8945 F8 |mov ,eax ;eax的值放入变量y
00401045|.8B4D FC |mov ecx, ;变量x的值放入ecx
00401048|.83C1 01 |add ecx,0x1 ;exc自加1
0040104B|.894D FC |mov ,ecx ;ecx赋值到变量x
0040104E|.^ EB E6 \jmp short Test3.00401036 ;无条件跳转到while条件判断处
00401050|>8B55 F8 mov edx, ;最终y的值放入edx
00401053|.52 push edx ; /<%d>
00401054|.68 1C204200 push Test3.0042201C ; |format = "%d"
00401059|.E8 32000000 call Test3.printf ; \printf
0040105E|.83C4 08 add esp,0x8 ;恢复esp
00401061|.33C0 xor eax,eax ;eax清零
00401063|.5F pop edi ;恢复edi
00401064|.5E pop esi ;恢复esi
00401065|.5B pop ebx ;恢复ebx
00401066|.83C4 48 add esp,0x48 ;恢复局部变量空间
00401069|.3BEC cmp ebp,esp ;测试堆栈平衡
0040106B|.E8 A0000000 call Test3._chkesp ;调用调试信息
00401070|.8BE5 mov esp,ebp ;恢复esp
00401072|.5D pop ebp ;恢复ebp
00401073\.C3 retn ;返回ret add esp,4
while循环基本结构:
cmp ;判断while条件是否满足
jg ;不满足就跳出while循环
... ;while执行体
jmp ;无条件跳转到条件判断处
Release版本:
00401000/$B8 01000000 mov eax,1 ;eax赋值为1
00401005|.33C9 xor ecx,ecx ;清零ecx,计数之用
00401007|>03C8 /add ecx,eax ;eax加上ecx放入ecx
00401009|.40 |inc eax ;eax自加1
0040100A|.83F8 64 |cmp eax,64 ;和100做比较
0040100D|.^ 7E F8 \jle short Test3.00401007 ;如果不满足条件就跳回add ecx,eax
0040100F|.51 push ecx ;参数
00401010|.68 30704000 push Test3.00407030 ;ASCII "%d"
00401015|.E8 06000000 call Test3.00401020 ;printf函数
0040101A|.83C4 08 add esp,8 ;恢复esp
0040101D|.33C0 xor eax,eax ;eax清零
0040101F\.C3 retn ;返回retadd esp,4
while循环基本结构:
自动判断是否满足while条件,如果否就直接跳过while,这里满足
... ;while执行体
cmp ;判断while条件是否满足
jle ;不满足就跳出while循环
总结:根据反汇编两种不同的编译方式可以看出,编译器已经自动识别出原始条件是否满足首次while条件,如满足则直接执行while内容,不满足就直接跳过while。
___________________________________________________________________________________________________
[*]#include < stdio.h >
[*]int main()
[*]{
[*] int x;
[*] do
[*] {
[*] printf("请输入一个数字");
[*] scanf("%d", &x);
[*] printf("你输入的数字是:%d\n", x);
[*] } while ( x != 0 );
[*] return 0;
[*]}
[*]
复制代码
___________________________________________________________________________________________________
上面程序是判断用户的输入数值,不等于0就一直输入,直到等于0就跳出do-while循环
Debug版本:
00401010 |> \55 push ebp ;ebp入栈保存
00401011 |.8BEC mov ebp,esp ;esp保存到ebp
00401013 |.83EC 44 sub esp,44 ;开辟局部变量空间
00401016 |.53 push ebx ;ebx入栈保存
00401017 |.56 push esi ;esi入栈保存
00401018 |.57 push edi ;edi入栈保存
00401019 |.8D7D BC lea edi,dword ptr ss: ;设置CC操作起始地址
0040101C |.B9 11000000 mov ecx,11 ;设置循环次数
00401021 |.B8 CCCCCCCC mov eax,CCCCCCCC ;赋值 eax CCint 3
00401026 |.F3:AB rep stos dword ptr es: ;循环复制 CC
00401028 |>68 B82F4200 /push Test3.00422FB8 ; /参数入栈
0040102D |.E8 5E000000 |call Test3.00401090 ; \调用printf函数
00401032 |.83C4 04 |add esp,4 ;恢复esp
00401035 |.8D45 FC |lea eax,dword ptr ss: ;取变量x的地址到eax
00401038 |.50 |push eax ; /参数入栈
00401039 |.68 1C204200 |push Test3.0042201C ; |Arg1 = 0042201C ASCII "%d"
0040103E |.E8 BDE80000 |call Test3.0040F900 ; \调用scanf函数
00401043 |.83C4 08 |add esp,8 ;恢复esp
00401046 |.8B4D FC |mov ecx,dword ptr ss: ;变量x的值放入ecx
00401049 |.51 |push ecx ; /参数入栈
0040104A |.68 A42F4200 |push Test3.00422FA4 ; |参数入栈
0040104F |.E8 3C000000 |call Test3.00401090 ; \调用printf函数
00401054 |.83C4 08 |add esp,8 ;恢复esp
00401057 |.837D FC 00 |cmp dword ptr ss:,0 ;判断x值是否为0
0040105B |.^ 75 CB \jnz short Test3.00401028 ;不为0即跳到到do循环出执行
0040105D |.33C0 xor eax,eax ;eax清零
0040105F |.5F pop edi ;恢复edi
00401060 |.5E pop esi ;恢复esi
00401061 |.5B pop ebx ;恢复ebx
00401062 |.83C4 44 add esp,44 ;恢复局部变量空间
00401065 |.3BEC cmp ebp,esp ;测试堆栈平衡
00401067 |.E8 A4000000 call Test3.00401110 ;调用调试信息
0040106C |.8BE5 mov esp,ebp ;恢复esp
0040106E |.5D pop ebp ;恢复ebp
0040106F \.C3 retn ;返回retadd esp,4
do-while基本结构:
.... ;do循环执行体
cmp ;条件判断
jnz ;如果不满足条件就跳出循环
Release版本:
00401000/$51 push ecx ;ecx入栈保存
00401001|>68 48804000 /push Test3.00408048 ;参数
00401006|.E8 4C000000 |call Test3.00401057 ;printf函数
0040100B|.8D4424 04 |lea eax,dword ptr ss: ;取变量x地址到eax
0040100F|.50 |push eax ;参数入栈
00401010|.68 44804000 |push Test3.00408044 ;ASCII "%d"
00401015|.E8 26000000 |call Test3.00401040 ;scanf函数
0040101A|.8B4C24 0C |mov ecx,dword ptr ss: ;变量值赋值到ecx
0040101E|.51 |push ecx ;参数
0040101F|.68 30804000 |push Test3.00408030 ;参数
00401024|.E8 2E000000 |call Test3.00401057 ;printf函数
00401029|.8B4424 14 |mov eax,dword ptr ss: ;变量值赋值到eax中
0040102D|.83C4 14 |add esp,14 ;恢复esp
00401030|.85C0 |test eax,eax ;判断eax是否为零
00401032|.^ 75 CD \jnz short Test3.00401001 ;如果不为零,就跳回执行
00401034|.33C0 xor eax,eax ;eax清零
00401036|.59 pop ecx ;恢复ecx
00401037\.C3 retn ;返回 retadd esp,4
do-while基本结构:
.... ;do循环执行体
test ;条件判断
jnz ;如果不满足条件就跳出循环
两种编译模式的汇编判断流程很相似,这也就是do-while和while的区别,不管是否满足条件,都会先执行一次再做判断!
___________________________________________________________________________________________________
goto语句:
[*]#include < stdio.h >
[*]int main()
[*]{
[*] int a;
[*] qq: printf("请输入一个数字:");
[*] scanf("%d", &a);
[*] if (a == 0)
[*] goto qq;
[*] return 0;
[*]}
复制代码
___________________________________________________________________________________________________
上面程序是判断用户输入,如果值为0就跳回重新输入判断,这里使用了goto语句
Debug版本:
00401010 >|> \55 push ebp ;保存ebp
00401011|.8BEC mov ebp,esp ;保存esp到ebp
00401013|.83EC 44 sub esp,44 ;开辟局部变量空间
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,11 ;设置循环次数
00401021|.B8 CCCCCCCC mov eax,CCCCCCCC ;赋值 eax CCint 3
00401026|.F3:AB rep stos dword ptr es: ;循环复制 CC
00401028|>68 20504200 /push offset Test3.??_C@_0BB@FLPH@?G?k?J?d?H?k?R?$LL?$LI?>;参数
0040102D|.E8 AE000000 |call Test3.printfgvdbgind_blockeressges ;printf函数
00401032|.83C4 04 |add esp,4 ;恢复esp
00401035|.8D45 FC |lea eax, ;取变量地址到eax
00401038|.50 |push eax ;参数
00401039|.68 1C504200 |push offset Test3.??_C@_02MECO@?$CFd?$AA@4k?5at?50x?$CF0>;ASCII "%d"
0040103E|.E8 3D000000 |call Test3.scanfalloc_basee_block_pages ;scanf函数
00401043|.83C4 08 |add esp,8 ;恢复esp
00401046|.837D FC 00 |cmp ,0 ;比较是否为0
0040104A|.75 02 |jnz short Test3.0040104E ;为0就执行if语句内容
0040104C|.^ EB DA \jmp short Test3.00401028 ;jmp无条件跳转
0040104E|>33C0 xor eax,eax ;eax清零
00401050|.5F pop edi ;恢复edi
00401051|.5E pop esi ;恢复esi
00401052|.5B pop ebx ;恢复ebx
00401053|.83C4 44 add esp,44 ;恢复局部变量空间
00401056|.3BEC cmp ebp,esp ;测试堆栈平衡
00401058|.E8 03010000 call Test3.__chkespleBuffers@4ingsW@4location?5size?3?5?$>;调用调试信息
0040105D|.8BE5 mov esp,ebp ;恢复esp
0040105F|.5D pop ebp ;恢复ebp
00401060\.C3 retn ;返回retadd esp,4
Release版本:
00401000/$51 push ecx ;ecx入栈保存
00401001|.68 34804000 push Test3.00408034 ;参数
00401006|.E8 5C000000 call Test3.00401067 ;printf函数
0040100B|.8D4424 04 lea eax,dword ptr ss: ;取变量地址到eax
0040100F|.50 push eax ;参数
00401010|.68 30804000 push Test3.00408030 ;ASCII "%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|.85C0 test eax,eax ;测试eax是否为0
00401023|.75 24 jnz short Test3.00401049 ;不满足就跳出if语句
00401025|>68 34804000 /push Test3.00408034 ;参数
0040102A|.E8 38000000 |call Test3.00401067 ;printf函数
0040102F|.8D4C24 04 |lea ecx,dword ptr ss: ;取变量地址到ecx
00401033|.51 |push ecx ;参数
00401034|.68 30804000 |push Test3.00408030 ;ASCII "%d"
00401039|.E8 12000000 |call Test3.00401050 ;scanf函数
0040103E|.8B4424 0C |mov eax,dword ptr ss: ;变量值赋值到eax
00401042|.83C4 0C |add esp,0C ;恢复esp
00401045|.85C0 |test eax,eax ;测试eax是否为0
00401047|.^ 74 DC \je short Test3.00401025 ;满足就跳回执行if语句
00401049|>33C0 xor eax,eax ;eax清零
0040104B|.59 pop ecx ;恢复ecx
0040104C\.C3 retn ;返回retadd esp,4
goto语句框架:
在debug版本中,goto语句等价鱼jmp指令,但在这个Release版本中却没有goto语句的影子,原因在于由于if语句的原因,这里直接忽略了goto语句,而是把goto语句对应的内容直接拷贝到了if语句下!换种情况,也是jmp指令!
___________________________________________________________________________________________________
本节主要对程序的流程化控制逆向分析完毕,各位私下还需要多多逆向联系,对日后大有帮助!好了,基础篇到此为止,中级篇主要讲解数组,指针,函数,以及类和对象...
页:
[1]