一、IF语句
-
三种格式:
if if...else... if..else if..else if....else
-
可以嵌套使用
二、IF反汇编指令
-
满足的格式:一般查看反汇编指令时,发现有一块指令修改了标志寄存器,后面紧跟了一个JCC指令,多半就是IF语句
-
而且注意:if后面的判断条件翻译成汇编指令逻辑是反的!!
-
比如我们写正向代码时使用的是
if(x>y)
表示如果x大于y则进入到{}
中执行当中的代码,如果不成立则不执行 -
但是当翻译成汇编指令后,
x>y
翻译的JCC指令为JLE
即有符号数小于等于则跳转到后面的地址,但是结合跳转的地址可以发现,汇编指令的意思为:如果x<=y,则跳转到后面的地址处,不执行if{}
当中的指令。所以虽然是一个意思,但是逻辑上是反的
-
-
我们逆向这种if语句时,只要知道它的执行条件即可,只要知道功能自己用C语言描述出来即可,不一定非要按照JCC一模一样翻译,要灵活。比如如果现在反汇编出现jle,那么jle虽然是小于等于则跳转到地址,但是我们写C语言是为了使用条件判断去执行当中的语句,所以应该翻译成如果大于则执行if当中的语句,如果小于等于则不执行,跳过执行此
if{}
中部分往下继续执行
三、IF…ELSE反汇编指令
-
满足的格式:同样先有一段影响标志寄存器的指令,下面跟着一个JCC跳转指令,跳转后顺序执行就可以到END结束位置;IF…ELSE反汇编只是后面还可以找到一句JMP指令且在JCC跳转的地址上面一行,JMP也会跳到END结束位置
修改标志寄存器指令 ------- JCC 地址1 | | ... //执行相关操作 ↓ jmp 结束地址 -------- 地址1: ... //执行相关操作 | ↓ ↓ 结束地址: ... ←-------------
四、IF…ELSE IF…ELSE反汇编指令
-
格式:发现有修改标志寄存器的指令下面跟着一个JCC跳转指令,跳转的地址又是一个修改寄存器然后紧跟JCC指令且上面是一个jmp指令;第二个JCC指令跳转的地址又是修改寄存器后紧跟JCC指令且上面是一个jmp指令。。。直到最后一个操作数据的指令执行完后就是结束地址。且发现上述所有的jmp指令跳转的地址都是结束地址
五、数组的初识
这里先提一下数组是如何定义和简单的使用的,因为我们做题需要用到
- 数组也是一种数据类型:它当中可以存储多个同类型的值。如果给数组定义了大小n,那么从前往后下标依次从0到n-1
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
void Function()
{
int x = arr[0]; //读取数组中的某一个位值
int y = arr[1];
int z = arr[2];
arr[3] = 20; //修改数组
}
六、作业
1.正向IF语句练习
-
定义4个int类型的全局变量,分别是g_x、g_y、g_z、g_r,使用if…else…分支语句,将最大的值存储到g_r中
#include "stdafx.h" //定义全局变量 int g_x; //全局变量不赋初始值默认为0 int g_y; int g_z; int g_r; //储存结果 //定义函数来返回三个数的最大值 int maxMethod(int a,int b,int c){ g_x = a; g_y = b; g_z = c; if(g_x >= g_y){ if(g_x >= g_z){ g_r = g_x; }else{ g_r = g_z; } }else{ if(g_y >= g_z){ g_r = g_y; }else{ g_r = g_z; } } return g_r; } int main(int argc, char* argv[]){ printf("%d\n",maxMethod(5,2,7)); //7 printf("%d",maxMethod(6,6,3)); //6 return 0; }
-
找出数组里面最大的值,并存储到全局变量中(if…esle)
#include "stdafx.h" //定义全局变量 int arr[4] = {2,5,7,9}; //4表示大小 int g_r; int ArrMax(int arr[]){ //定义一个整型数组型参数,名字可以任意 if(arr[0] >= arr[1]){ if(arr[0] >= arr[2]){ if(arr[0] >= arr[3]){ g_r = arr[0]; }else{ g_r = arr[3]; } }else{ if(arr[2] >= arr[3]){ g_r = arr[2]; }else{ g_r = arr[3]; } } }else{ if(arr[1] >= arr[2]){ if(arr[1] >= arr[3]){ g_r = arr[1]; }else{ g_r = arr[3]; } }else{ if(arr[2] >= arr[3]){ g_r = arr[2]; }else{ g_r = arr[3]; } } } return g_r; } int main(int argc, char* argv[]){ printf("%d",ArrMax(arr)); //把全局变量传给ArrMax方法的参数 return 0; }
-
将数组所有的元素相加,将结果存储到g_r中
#include "stdafx.h" int arr[10] = {2,5,7,9,22,4,8,22,3,18}; int g_r; //默认值为0 //使用while int ArrSum(int arr[]){ int i = 0; while(i <= 9){ g_r += arr[i]; i++; } return g_r; } //使用for int ArrSum2(int arr[]){ for(int i = 0;i <= 9;i++){ g_r += arr[i]; } return g_r; } int main(int argc, char* argv[]){ printf("%d\n",ArrSum(arr)); g_r = 0; //方法一得出结果后要把g_r清零再调用方法二 printf("%d",ArrSum2(arr)); return 0; }
-
有一个字符串是这样的:china中国verygood天朝nice,里面既含有中文又含义英文,请编写一个函数,能截取任意长度的字符串n(n<=总长度)
比如:subString(5) = china;subString(6) = china中;subString(8) = china中国v
因为我们知道一个英文字母存储在内存中占一个字节(ASCII编码),一个汉字存储在内存中占两个字节(GB)
#include "stdafx.h" //全局变量 char str[] = "china中国verygood天朝nice"; void subString(char str[],int length){ int count = 0; //因为length表示我们想要得到的字符个数,所以使用count计数,到达个数就停止 for(int i = 0;count < length && str[i] != '\0';i++,count++){ int unsigned temp = str[i]; //得出它的对应的编码数,一定要注意是无符号数!我们在意的是大小 if(temp <= 127){ //因为标准ASCII码没有超过127的,即7bit。所以<127表示数组当前位置存的字母 printf("%c",str[i]); }else{ //如果编号大于127,那么说明这是一个汉字的一半(GB编码两字节表示一个汉字) char tempStr[] = "周"; //如果不知道定义多大的宽度数组,就干脆初始化定义一个随机汉字即可 tempStr[0] = str[i++]; tempStr[1] = str[i]; printf("%s",tempStr); //采用这种字符串拼接的方法,来达到两个字节输出一个汉字 } } } int main(int argc, char* argv[]){ //验证 subString(str,5); printf("\n"); subString(str,6); printf("\n"); subString(str,8); return 0; }
2.逆向IF语句
1)IF逆向
-
反汇编如下:
调用处代码: push 5 push 4 call 0040100f add esp,8 ------------------------------------------------------------------------------- 函数内部: 00401030 push ebp 函数内部功能分析: 00401031 mov ebp,esp 1、分析参数: 00401033 sub esp,44h 00401036 push ebx 00401037 push esi 00401038 push edi 2、分析局部变量 00401039 lea edi,[ebp-44h] 0040103C mov ecx,11h 00401041 mov eax,0CCCCCCCCh 00401046 rep stos dword ptr [edi] 00401048 mov eax,[004225c4] 3、分析全局变量 0040104D mov dword ptr [ebp-4],eax 00401050 mov ecx,dword ptr [ebp+8] 00401053 cmp ecx,dword ptr [ebp+0Ch] 00401056 jg 00401064 00401058 mov edx,dword ptr [ebp+0Ch] 4、功能分析 0040105B add edx,dword ptr [ebp-4] 0040105E mov dword ptr [004225c4],edx 00401064 pop edi 00401065 pop esi 5、返回值分析 00401066 pop ebx 00401067 mov esp,ebp 00401069 pop ebp 0040106A ret 6、还原成C函数
-
按照上述步骤开始分析:
#include "stdafx.h" int m; //全局变量 void Func1(int x,int y){ //参数 int a; //局部变量 a = m; if(x<=y){ m = y + a; } } int main(int argc,char* argv[]){ Func1(4,5); //看参数入栈的指令,注意是倒着入栈的 }
-
验证一下:
2)IF…ELSE逆向
-
反汇编如下:
004010B0 push ebp 函数内部功能分析: 004010B1 mov ebp,esp 004010B3 sub esp,48h 1、分析参数: 004010B6 push ebx 004010B7 push esi 004010B8 push edi 004010B9 lea edi,[ebp-48h] 2、分析局部变量 004010BC mov ecx,12h 004010C1 mov eax,0CCCCCCCCh 004010C6 rep stos dword ptr [edi] 004010C8 mov eax,[004225c4] 004010CD mov dword ptr [ebp-4],eax 3、分析全局变量 004010D0 mov dword ptr [ebp-8],2 004010D7 mov ecx,dword ptr [ebp+8] 004010DA cmp ecx,dword ptr [ebp+0Ch] 004010DD jl 004010e8 004010DF mov edx,dword ptr [ebp-8] 4、功能分析 004010E2 add edx,1 004010E5 mov dword ptr [ebp-8],edx 004010E8 mov eax,dword ptr [ebp+8] 004010EB cmp eax,dword ptr [ebp+0Ch] 5、返回值分析 004010EE jge 004010fb 004010F0 mov ecx,dword ptr [ebp-8] 004010F3 mov dword ptr [004225c4],ecx 004010F9 jmp 00401107 6、还原成C函数 004010FB mov edx,dword ptr [ebp-4] 004010FE add edx,dword ptr [ebp-8] 00401101 mov dword ptr [004225c4],edx 00401107 pop edi 00401108 pop esi 00401109 pop ebx 0040110A mov esp,ebp 0040110C pop ebp 0040110D ret
-
按照上述步骤开始逆向分析:
#include "stdafx.h" int m; void Func(int x,int y){ int a = m; int b = 2; if(x >= y){ b++; } if(x < y) { m = b; }else{ m = a + b; } } int main(int argc,char* argv[]){ Func(1,2); return 0; }
-
验证一下:
3)IF…ELSE IF…ELSE逆向
-
反汇编如下:
004010B0 push ebp 函数内部功能分析: 004010B1 mov ebp,esp 004010B3 sub esp,4Ch 1、分析参数: 004010B6 push ebx 004010B7 push esi 004010B8 push edi 004010B9 lea edi,[ebp-4Ch] 2、分析局部变量 004010BC mov ecx,13h 004010C1 mov eax,0CCCCCCCCh 004010C6 rep stos dword ptr [edi] 004010C8 mov dword ptr [ebp-4],0 004010CF mov dword ptr [ebp-8],1 3、分析全局变量 004010D6 mov dword ptr [ebp-0Ch],2 004010DD mov eax,dword ptr [ebp+8] 004010E0 cmp eax,dword ptr [ebp+0Ch] 004010E3 jg 004010f0 004010E5 mov ecx,dword ptr [ebp-8] 4、功能分析 004010E8 sub ecx,1 004010EB mov dword ptr [ebp-4],ecx 004010EE jmp 00401123 004010F0 mov edx,dword ptr [ebp+0Ch] 5、返回值分析 004010F3 cmp edx,dword ptr [ebp+10h] 004010F6 jl 00401103 004010F8 mov eax,dword ptr [ebp-0Ch] 004010FB add eax,1 6、还原成C函数 004010FE mov dword ptr [ebp-4],eax 00401101 jmp 00401123 00401103 mov ecx,dword ptr [ebp+8] 00401106 cmp ecx,dword ptr [ebp+10h] 00401109 jle 00401116 0040110B mov edx,dword ptr [ebp-8] 0040110E add edx,dword ptr [ebp-0Ch] 00401111 mov dword ptr [ebp-4],edx 00401114 jmp 00401123 00401116 mov eax,dword ptr [ebp-0Ch] 00401119 mov ecx,dword ptr [ebp-8] 0040111C lea edx,[ecx+eax-1] 00401120 mov dword ptr [ebp-4],edx 00401123 mov eax,dword ptr [ebp-4] 00401126 add eax,1 00401129 pop edi 0040112A pop esi 0040112B pop ebx 0040112C mov esp,ebp 0040112E pop ebp 0040112F ret
-
按照上述步骤开始分析:
#include "stdafx.h" int Func(int x,int y,int z){ int a = 0; int b = 1; int c = 2; if(x<=y){ a = b - 1; }else if(y >= z){ a = c + 1; }else if(x > z){ a = b + c; }else{ a = b + c - 1; } return a + 1; } int main(int argc,char* argv[]){ Func(1,2,3); return 0; }
-
验证一下:
3.总结
-
IF语句反汇编格式:就只有单纯的这种格式
影响标志寄存器的指令 JCC指令
-
if…else语句反汇编格式:
影响标志寄存器的指令 JCC指令 1.如果不跳转一直走到jmp,jmp后面的地址为if分支语句结束的地方 2.如果跳转,跳转到的地方没有影响寄存器指令,而是一些操作指令,执行完后就结束了if分支语句
-
if…else if…else语句反汇编格式:
影响标志寄存器的指令 JCC指令 1.如果不跳转,一直走到jmp,jmp后面的地址为分支结束地址 2.如果跳转 2.1如果发现还有影响标志寄存器的指令,且上面一行是jmp指令,下面又分为跳与不跳...则此处时else if语句 2.2如果发现没有影响标志寄存器指令了,而是一些操作指令,执行完后就结束了if分支语句,则是else语句 满足的共性: 1.当每个条件跳转指令要跳转的地址前面都有jmp指令 2.这些jmp指令跳转的地址都是一样的 3.如果某个分支没有条件判断,则为else部分
-
逆向分析的时候不要一头扎进去一步一步的分析,而是先要按照这些分支语句的特征分析出哪些部分是if,哪些部分是else if,哪些部分是else,即先做整体分析,分析出结构后再去具体分析!