Win32反汇编(五)C/C++中的 if-else 与 switch-case 的正向分析与反向分析

前言

作者:浪子花梦,一个有趣的程序员 ~
此系列文章都是一些基础的文章,每篇文章都通过几个小例子快速的了解 Win32反汇编与OD的使用,在此作个笔记
如若对您有帮助,记得三连哟 ~


前文链接

Win32反汇编(一) 初步探索Win32反汇编 与 Ollydbg的简单使用
Win32反汇编(二)几种常见的指令反汇编详解:EAX、MOVSX与MOVZX、LEA、SUB、CMP与转移指令
Win32反汇编(三)深层次的了解各种转移指令:IF语句有符号与无符号跳转
Win32反汇编(四)栈的工作原理与堆栈平衡,函数方法参数的调用约定


文章目录

先对C/C++中的 if-else、switch-case 语句进行分析,然后通过汇编指令反推出高级语言中的语句 . . .


在这里插入图片描述

if-else 汇编指令分析

直接开始,go!!! cpp 代码如下所示:

#include <cstdio>

int main() {
	printf("begin\n");

	int a = 1;
	int b = 2;
	int c = 3;

	if (a > b) {
		printf("a>b\n");

		if (c > a) {
			printf("%d>%d, %d>%d\n", c, a, a, b); 
		}
		else {
			printf("%d<=%d, %d>%d\n", c, a, a, b);
		}
	}
	else {
		printf("a<=b\n");
	}
	  

	return 0;
}

在第一个 if中还嵌套着另一个if-else语句,我们在OD调试的时候注意一下,下面是使用 OD反汇编的情况,如下所示:
在这里插入图片描述
反汇编的结果很长,我们需要慢慢的分析,首先在程序中定义了三个局部变量,如下所示:
在这里插入图片描述

然后通过下面的两条语句进行第一个if的判断,如下所示:

007F10A8 |. 8B45 FC mov eax, dword ptr [ebp-4]
007F10AB |. 3B45 F8 cmp eax, dword ptr [ebp-8]

如果 a <= b,则第一个if-else 执行 else部分的代码,通过下面这条指令进行跳转:

007F10AE |. /7E 53 jle short 007F1103

跳转的内容如下所示(调用printf函数,然后进行堆栈平衡):
在这里插入图片描述

如果 a > b 成立的话,则执行第一个 if中的语句,对应的汇编指令如下所示:
在这里插入图片描述
而这些指令中又进行了一个 if-else语句,所以与上面的判断类似,下面我就将一些注释写下来,方便理解:
在这里插入图片描述

.
.


if-else 逆向分析

下面我们通过一些汇编指令来还原C/C++中的 if-else语句 . . .

首先,我们大概看一下汇编指令是什么样子的,如下所示:
在这里插入图片描述

乍一看有很多的指令,但是非常的好理解,上面有许多的跳转指令,和printf函数,下面我们就好好的分析一下这些指令,然后将它还原成高级语言中的语句 . . .
.

分析过程

1)程序刚开始时,会执行一条 printf语句,然后会定义三个局部变量,如下所示:
在这里插入图片描述
.
2)首先会比较 a 与 b,成立条件是 a < b,会执行printf函数,如下所示:
在这里插入图片描述
.
3)不成立时会跳转到 jge指令所指的地址,发现这些指令又进行了一次判断,成立则执行 printf函数,如下所示(因为他们之间有着关联,所以用 else if连接):
在这里插入图片描述
.
4)else if还不成立,则执行 jnz指令跳转,如下所示,已经没有了 CMP指令了,所以用 else连接:
在这里插入图片描述
.
5)程序大体的框架就是这么一个 if-else语句,但是我们还没有分析这些条件成立后的语句,所以下面我们来一个语句块慢慢的分析,先分析 第一个if条件 成立后发生的事情,对 b和c进行比较,成立条件是 b > c,成立后会立即执行 printf函数,如下所示:
在这里插入图片描述
.
6)如果 b > c不成立,则执行 jle指令进行跳转,继续判断 b == c是否成立,如果成立则执行printf函数,如下所示:
在这里插入图片描述
.
7)如果 b == c也不成立,则执行 jnz指令继续跳转,执行printf函数,我们发现没有指令继续比较或者跳转了,所以用 else连接,如下所示:
在这里插入图片描述
.
8)最外层的 else if语句中执行 b == c判断,如果成立,则执行printf,如果不成立则执行jnz指令跳转,执行 printf:
在这里插入图片描述
.
9)最后执行一条 printf函数:
在这里插入图片描述
整个程序的逆向分析就完成了,结果应该是正确的,没有提前看过答案,下面我们来介绍一下 switch case的分析吧 . . .
.
.


switch-case 汇编指令分析

switch 语句反汇编时,被翻译成两个版本,版本之间的区别是根据 case的个数来区别的,我们先来看一下第一个简单的版本, cpp代码如下所示:

#include <cstdio>

int main() {

	printf("start\n");	

	int a = 3;

	switch (a)
	{
	case 1:
		printf("a=1");
		break;
	case 2:
		printf("a=2");
		break;
	case 3:
		printf("a=3");
		break;
	default:
		printf("default");
		break;
	}

	printf("\nend");	

	return 0;
}

其中有4个case,下面我们用 OD来调试一下,如下所示:
在这里插入图片描述
我们发现他是用一个数与其它数分别进行比较,如果相同,则跳转到对应的指令操作,如下所示:
在这里插入图片描述
跳转到执行指令如下所示:
在这里插入图片描述
执行完对应的指令后,执行 jmp指令无条件进行跳转,这样就不会执行到其它的地方了,对应C中的 break 语句 . . .

下面我们看一下第二种版本的 switch反汇编,cpp 文件如下所示:

#include <cstdio>
#include <climits>

int main() { 

	printf("start\n");	

	int a = 25;

	switch (a)
	{
	case 11:printf("****\n"); break;
	case 16:printf("&&&&\n"); break;
	case 30:printf("3333\n"); break;
	case 25:printf("6666\n"); break;
	case 19:printf("&&&&\n"); break;
	case 20:printf("&&&&\n"); break;

	default:printf("default\n"); break;
	}

	printf("end\n");

	return 0;
}

我们发现用了好几个 case,我们下面来看看 反汇编中它是怎么工作的,如下所示:
在这里插入图片描述

我们发现上面只有一个 ja指令了,而且上面做了许多令我们萌币的操作,这是什么意思呢? 下面让我来带大家了解一下,这其实是运用了一个叫定位表的东西 . . .

首先,先将我们传入的25减去case中的最小值,如下所示:
在这里插入图片描述

然后用E和case中最大值与最小值的差 作比较,如下所示:
在这里插入图片描述
这样做的目的很简单,就是判断我们传入的这个数是否比较case中的最小数小,最大数大,若成立,则该switch不执行,执行 je指令实现 . . .

然后程序会通过下面这两行指令实现向定位表中获取对应的执行地址,如下所示:
在这里插入图片描述

定位表如下所示,通过 mov edx, dword ptr [ebp-4] 获取其中的值:
在这里插入图片描述
然后通过从定位表中获取的值,再通过 movzx eax, byte ptr [edx+321158] 指令实现获取想要执行的地方的地址,如下所示:
在这里插入图片描述
圈起来的每一个地址,对应着一个 case中的语句,如下所示:
在这里插入图片描述

最终获取的地址是 003210ED,执行的指令是:
在这里插入图片描述

下面我们通过一个汇编程序,反推出C中的Switch语句 . . .

.
.


switch-case 逆向分析

汇编代码如下所示,我们需要做的事情就是将它还原于 C代码:

在这里插入图片描述
.
1)首先我们可以根据前面的几行指令推断出传入switch中的参数,还有switch中case的最小值与最大值,如下所示:
在这里插入图片描述
.
2)我们主要分析的就是定位表中的偏移位置推断,其实这件事非常的好做,程序给出好的地址如下所示:
在这里插入图片描述
.
3)我们来观察下面的两条指令,如下所示:

  • movzx eax, byte ptr [edx+A91158]
  • jmp dword ptr [eax*4+A9113C]
    首先通过 edx获取到eax的值,然后通过 eax获取一个内存单元中的值(这边可以判断出内存单元中的值是一系列地址). . .

edx + A91158 代表的地址及数据如下所示:
在这里插入图片描述
这就是一个定位表,也就是说我们通过这个定位表获取到一个偏移值,0 ~ 6 . . .

eax*4+A9113C 所代表的地址及其中的数据如下所示:
在这里插入图片描述
不管 eax取到 0 ~ 6中的哪一个值,都能获取对应内存单元中的一个值,然后跳转到目标指令执行 . . .

现在我们要分别计算当 eax 等于 0 ~ 6 中的某一个值时, edx的值为多少? 当然,当 eax == 6时,肯定是default,因为上面太多数都是6,而且 eax == 0时也不需要计算, 当eax == 0时,就是case的最小值,上面我们已经推断出来了,是 6,而且对应的最大值我们也求出来了,是 0x29 == 41,应该是 eax 等于 5的时候,所以我们只需要求 1 ~ 4这个范围的 edx就行了,下面我只求出 eax == 4时的 edx值,其它的都是一样的 . . .

当 eax == 4 时,通过 movzx eax, byte ptr [edx+A91158] 这条指令,我们只需要知道 byte ptr [edx+A91158] 这个内存单元的地址在什么地方,然后减去 A91158 就能算出 edx的值了,如下所示:
在这里插入图片描述
edx = A91179 - A91158 = 0x21,而 edx又减去了 6,所以 edx的值为 0x27 == 39,所以这个 case就是 39,并且执行对应的操作指令,如下所示:

在这里插入图片描述
从 0 开始数,第4个就是获取的地址,并执行 jmp跳转 . . . 其它的eax值发生的也是一样的 . . .

.
.


  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值