《逆向工程核心原理》学习笔记(一):代码逆向技术基础

本文介绍了逆向工程的基础知识,包括代码逆向技术、调试器使用技巧、IA-32寄存器讲解、栈帧概念及函数调用约定等内容。通过HelloWorld程序和abex’crackme案例详细展示了逆向分析的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

开始学习逆向
从《逆向工程核心原理》这本经典开始
本篇笔记是第一部分:代码逆向技术基础

一、关于逆向工程

1、代码逆向工程

代码逆向工程(Reverse Code ENgineering, RCE)(吐槽下,英文缩写真的太乱了,代码执行漏洞也是RCE)

  • 静态分析:不执行代码文件
  • 动态分析:调试分析代码流

然后是些鸡汤
建议循序渐进
不要急躁贪婪
共勉

二、逆向分析hello world程序

运行hello world

在这里插入图片描述

1、调试程序:熟悉基础指令

扔进OD
目标是寻找main函数

在这里插入图片描述
入口点(EntryPoint, EP)
win可执行文件的代码入口点,依赖于CPU

在这里插入图片描述
依次是地址、指令、汇编代码、注释

OD基本指令

在这里插入图片描述
用F7进入CALL命令

在这里插入图片描述
执行JMP命令

在这里插入图片描述
从40104F开始
用上面的基本指令进行调试查看
寻找main函数

最终在40100有所发现

在这里插入图片描述

2、进一步熟悉调试器:更多指令和大本营

指令

在这里插入图片描述
设置大本营的方法

  • cril+G定位地址,F4让调试流到定位处
  • F2设置断点(BP)
  • ;添加注释,通过查找命令找到
  • :输入标签

3、查找指定代码

(1)代码执行法

即上面使用的方法
适用于调试代码量不大且程序功能明确的情况

不断F8寻找即可

(2)字符串检索法

右键 -> Search for -> All referenced text strings

在这里插入图片描述

(3)API检索法

右键 -> Search for -> All intermodular calls

由于程序跳出了消息窗口
推断调用了user32.MessageBoxW()API

在这里插入图片描述

有时候OD不能列出所有API
那就向DLL代码库寻找
右键 -> Search for -> Name in all modules

在这里插入图片描述

4、打补丁修改字符串

(1)直接修改字符串缓冲区

即在dump直接修改
ctrl+G找到位置
ctrl+E修改

在这里插入图片描述
在这里插入图片描述

但是这对字符串有长度限制
且只是暂时的
若要真正修改,需要做保存

(2)内存里新建字符串并传递给消息函数

在程序中找空白填写字符串
然后改main函数里的push地址

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

5、小结:常用指令

借hello world简单感受下调试

指令如下

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、小端序标记法

字节序:多字节数据的储存顺序

  • 小端序(little endian):内存地址低位存储数据的低位,内存地址高位存储数据的高位,逆序。常用于Intel x86 CPU
  • 大端序(big endian):内存地址低位存储数据的高位,内存地址高位存储数据的低位,直观。常用于大型UNIX服务器的RISC系列的CPU、网络协议

在这里插入图片描述

四、IA-32寄存器基本讲解

IA-32(Intel Architecture 32位)寄存器

  • 通用寄存器:32位,8个
  • 段寄存器:16位,6个
  • 程序状态与控制寄存器:32位,1个
  • 指令指针寄存器:32位,1个

在这里插入图片描述

1、通用寄存器

通用寄存器共8个,且在16位时就存在,故加E表示扩展

  • EAX:累加器,针对操作数和结果数据,所有win32 API函数都会把返回值保存在此
  • EBX:基址寄存器,DS段中的数据指针
  • ECX:计数器,字符串和循环操作,每循环一次减1
  • EDX:数据寄存器,I/O指针
  • EBP:扩展基址指针寄存器,SS段中栈内数据指针,表示栈区域的基地址
  • ESI:源变址寄存器,字符串操作源指针
  • EDI:目的变址寄存器,字符串操作目标指针
  • ESP:栈指针寄存器,SS段中栈指针,指示栈顶地址

在这里插入图片描述

2、段寄存器

  • 内存保护技术
  • 将内存划分为多个区段,并赋予起始地址、范围、访问权限
  • 和分页技术一起将虚拟内存变更为实际物理内存

段寄存器共6个,指向的段描述符与虚拟内存结合形成线性地址,借助分页技术(如果有的话),转为实际物理地址

  • CS:代码段寄存器,存放程序代码所在段的段基址
  • SS:栈段寄存器,存放栈段的段基址
  • DS:数据段寄存器,存放数据段的段基址
  • ES:附加段寄存器
  • FS:数据段寄存器,调试中常用,计算SEH\TEB\PEB
  • GS:数据段寄存器

在这里插入图片描述

3、程序状态与控制寄存器

名称为EFLAGS,标志寄存器
每一位都有意义,看图
重要的三位是

  • ZF:零标志,运算结果为0则置1
  • OF:溢出标志,有符号整数溢出或最高有效位改变,置1
  • CF:进位标志,无符号整数溢出,置1

在这里插入图片描述

4、指令指针寄存器

名称为EIP,指令指针寄存器

  • 保存CPU要执行的指令地址
  • 不能直接修改
  • 可以通过JMP、Jcc、CALL、RET、中断、异常来间接修改

五、栈

  • FILO原则
  • 高地址向低地址扩展
  • 特征:栈顶指针(ESP)初始状态指向栈底

在这里插入图片描述

六、分析abex’ crackme

这个abex’ crackme运行如下

在这里插入图片描述
在这里插入图片描述

1、OD打开

在这里插入图片描述
非常简短
因为这是直接汇编写的

逻辑非常简单清晰

  • 调用MessageBox API出现第一个窗口
  • 调用GetDriveType API获取C驱动器类型,然后操作它,使之被识别为CD-ROM类型
  • 对寄存器做些操作
  • 条件判断弹出调用成功或调用失败的窗口

2、破解

401026地址处指令
修改为JMP 0040103D
即直接改为跳转

在这里插入图片描述
在这里插入图片描述

七、栈帧

1、栈帧

栈帧在程序中用于声明局部变量、调用函数

  • 用EBP寄存器访问站内局部变量、参数、函数返回地址等
  • 调用函数时,将作为基准点的ESP值保存到EBP

栈帧结构如下:
在这里插入图片描述

2、示例:stackframe

以stackframe这个程序为例
感受下栈帧的使用
观察栈的行为动作

(1)生成栈帧

源码:

在这里插入图片描述

在main函数的起始地址401020设置断点

在这里插入图片描述
此时寄存器EBP和ESP如下:

在这里插入图片描述
运行完401020和401021两步后就生成了栈帧

(2)设置局部变量

首先是分配空间:从ESP减去8个字节,为函数的局部变量a, b开辟8个字节的空间

00401023  sub esp,0x8

然后是指针两个

在这里插入图片描述

执行完后的栈内如下

在这里插入图片描述

(3)add函数

在这里插入图片描述
这一段是把b和a压入栈,注意顺序与c语言相反
然后call401000的函数,即add

在这里插入图片描述

执行完之后
栈内

在这里插入图片描述

(4)printf函数

在这里插入图片描述

  • 地址0401044处的EAX寄存器中存储着函数add()的返回值,它是执行加法运算后的结果值3
  • 地址0040104A处的CALL 00401067命令中调用的是00401067地址处的函数,它是一个c标准库函数printf(),所有C标准库函数都有Visual C++编写而成
  • 由于上面的printf()函数有2个参数,大小为8个字节(32位寄存器+32位常量=64位=8字节),所以在0040104F地址处使用ADD命令,将ESP加上8个字节,把函数的参数从栈中删除
(5)删除栈帧

在这里插入图片描述
释放EBP
然后return

八、分析abex’ crackme #2

程序打开如下
在这里插入图片描述
在这里插入图片描述

使用Visual Basic写的

  • 使用msvbvm60.dll专用引擎
  • VB文件可以便以为本地代码和伪代码
  • 各种信息以结构体形式保存

1、OD打开

EP的地址是401238

  • 把RT_MainStruct结构体的地址401E14压入栈
  • 然后CALL命令调用JMP,跳转到VB引擎的主函数ThunRTMain(),这是VB常用的间接调用法

在这里插入图片描述

2、破解

通过字符串搜索找到“congratulation”

在这里插入图片描述

发现403332 的条件判断
往上面看,push的edx和eax是我们需要的参数
再往上看,找到栈帧
设置断点并调试

在这里插入图片描述

然后一步步调试

  • 找到记录Name的地方
  • 找到加密过程
    • 从Name逐一读取字符
    • 字符转换ASCII码
    • ASCII码加64
    • 转换为字符
    • 连接字符
  • 最后找到Serial=B6C9DAC9

在这里插入图片描述
这里过程太长了
就不一一记录
可以参考逆向工程核心原理学习笔记(二十七):abex’crackme #2 破解算法

九、Process Explorer

工具推荐
使用可参考
Process Explorer使用图文教程

十、函数调用约定

函数调用约定(Calling Convention),是对函数调用时如何传递参数的约定

  • 函数执行完成后,栈中的参数不用管,会被覆盖
  • 函数执行完成后,ESP要恢复到调用之前,不然ESP指向栈底,无法使用栈,这就是函数调用约定要解决的问题

1、cdecl

主要在C语言中使用,调用者负责处理栈

例子

#include "stdio.h"

int add(int a, int b)
{
	return (a+b);
}
int main(int argc, char* argv[])
{
	return add(1,2);
}

扔进OD可以看到

  • add函数的参数逆序压入栈
  • 然后用 ESP, 8 命令整理栈
  • main函数直接清理压入栈中的函数参数
    在这里插入图片描述

2、stdcall

常用于Win32 API,由被调用者清理栈
比cdecl的代码尺寸小

例子

#include "stdio.h"

int _stdcall add(int a, int b)
{
	return (a+b);
}
int main(int argc, char* argv[])
{
	return add(1,2);
}

扔进OD可以发现

  • 省略了 ESP, 8 命令
  • 用 retn 8 命令(retn + pop 8),使ESP增加到指定大小

在这里插入图片描述

3、fastcall

与stdcall类似,但通常用寄存器(而非栈内存)传递参数,若有4个参数,则前2个参数分别用ECX和EDX传递

由于使用寄存器,对函数的调用显然要快很多,但可能需要额外的系统开销来管理ECX和EDX

结语

主要是以下内容

  • OD的基础指令
  • 注意小端字节序
  • IA-32位寄存器的基本介绍
  • 栈的简单介绍
  • 栈帧的介绍
  • 函数调用约定

一个网站:https://www.tuts4you.com/

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值