【逆向】C与汇编的关系

程序1

使用 VC6.0 编译如下程序(使用 VC6.0 的原因是该编译器不会对代码进行过多的优化,因此适合逆向入门)

// 01.cpp : Defines the entry point for the console application.
//
# include "stdafx.h"

int main(int argc, char* argv[]) {
   
	printf("Hello World!\n");
	return 0;
}

得到的程序使用 OllyICE 打开,找到 main 函数位置

image-20230118231216589

image-20230118231651147

可以看到 VC 将 printf 编译成了一个常量,也就是启用了全局堆来存放。

执行到 printf 后,右键选择在数据窗口跟随,可以看到该区段堆的结构

image-20230118232213674

image-20230118232313042

接下来修改程序

// 01.cpp : Defines the entry point for the console application.
//
# include "stdafx.h"

int main(int argc, char* argv[]) {
   
	char buff[100] = {
   "sssssssssssss\0"};
	printf("Hello World!\n");
	return 0;
}

跟踪发现,VC 给程序分配了一段 0xA4 大小的堆栈空间,并且对这段空间进行了初始化

image-20230118233057366

选中寄存器区域的 ESP,右键在数据区跟随,到达堆栈。继续执行可以看到首先堆栈被初始化,然后分三次将 13 个 ‘s’ 写入内存

image-20230118233825260

程序2

再调试如下程序

// 01.cpp : Defines the entry point for the console application.
//
# include "stdafx.h"

int main(int argc, char* argv[]) {
   
	int b = 0;
	b = 15;
	int i = b + 1;
	printf("Hello World!\n");
	return 0;
}

首先程序保存原来 EBP 的值,并分配新的堆栈空间。然后保存原来 EDI 的值,并分配新的数据区,使用rep指令对新数据区初始化。

image-20230118235718623

使用内联汇编的方式编写程序并编译

#include "stdafx.h"
#include <windows.h>

int main(int argc, char* argv[]) {
   
	__asm{
   
		pushad
		pushfd
		MOV AL, 0xFF
		ADD AL, 1
		popad
		popfd
	}
	return 0;
}

可以看到,执行pushad会将所有寄存器的内容保存到栈里,执行pushfd会将所有标志位保存到栈里

image-20230119003246773

条件分支语句

下面再编译一个包含if-else的程序

#include "stdafx.h"
#include <windows.h>

int main(int argc, char* argv[]) {
   
	int a = 1;
	if (a == 1) {
   
		printf("%d\n", 11);
	} else {
   
		printf("%d\n", 22);
	}
	return 0;
}

image-20230119004330522

内联汇编

下面演示如何内联汇编。打开一段比较复杂的程序,比如 Wechat Setup.exe,跳过开头很多程序入口,假设想要使用如下一段汇编指令的原生态代码

image-20230119102103005

cdq
shld    edx,eax,0x10
shl     eax,0x10
mov     edi,eax
xor     esi,esi

接下来打开 VC2008,新建项目;勾选Visual C+±控制台应用程序,勾选预编译头;右键左侧项目名,选择属性-配置属性-C/C+±代码生成-运行时库,选择多线程(/MT),这样就可以将 VC 库打到 exe 文件里去了;使用 VC2008 生成可执行文件。

image-20230119103718790

// 04.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>

int __declspec (naked) Plus() {
   
	__asm{
   
		pushad
		cdq
		shld    edx,eax,0x10
		shl     eax,0x10
		mov     edi,eax
		xor     esi,esi

		popad
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
   
	Plus();
	return 0;
}

可以看到 VC2008 编译出文件的入口位置与 VC6.0 完全不一样

image-20230119104025070

找到唯一一个 call 语句后设置断点并步入,在下面找到 wmainCRTStartup,双击跳转

image-20230119104730172

双击 jump 语句跳转

image-20230119104906015

找到我们的 main 函数,并双击跳转

image-20230119105040207

image-20230119105106958

再双击跳转

image-20230119105208097

双击 call 指令跳转,找到我们定义的 Plus 函数

image-20230119105318508

双击跳转,找到我们写的裸函数内容

image-20230119105407198

可以看到完全没有堆栈初始化等内容,也就是说使用 naked 定义的裸函数,所有的堆栈初始化、保护恢复现场等,都是由我们自己来决定的。我们尝试不适用裸函数来定义一个函数。

#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>

int Plus() {
   
	int x = x + 1;
	return 1;
}

int _tmain(int argc, _TCHAR* argv[])
{
   
	Plus();
	return 0;
}

image-20230119110445297

image-20230119110554895

image-20230119110620371

image-20230119110636209

image-20230119110709587

可以看到编译器承包了所有工作。

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-01-03 14:56:06       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-01-03 14:56:06       106 阅读
  3. 在Django里面运行非项目文件

    2024-01-03 14:56:06       87 阅读
  4. Python语言-面向对象

    2024-01-03 14:56:06       96 阅读

热门阅读

  1. Spring Cloud Bus 相关面试题及答案(2024)

    2024-01-03 14:56:06       46 阅读
  2. python区别与C++的总结

    2024-01-03 14:56:06       52 阅读
  3. SpringBoot之注册Web组件

    2024-01-03 14:56:06       64 阅读
  4. python中xpath库知识点记录

    2024-01-03 14:56:06       46 阅读
  5. 二、C#基础语法( 委托与事件)

    2024-01-03 14:56:06       57 阅读
  6. 邦芒忠告:写个人简历时千万不要犯十个错误

    2024-01-03 14:56:06       60 阅读
  7. 服务器的固件和OS

    2024-01-03 14:56:06       57 阅读
  8. linux seq_file 文件编程步骤

    2024-01-03 14:56:06       39 阅读
  9. ntp校时服务器、ntp授时服务器、ntp时钟服务器

    2024-01-03 14:56:06       64 阅读
  10. 如何选择高防服务器

    2024-01-03 14:56:06       58 阅读