【转】《基于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]