0x12 逆向
1. 汇编与逆向
函数调用约定 | 调用约定 | 约定名称 | 传参方式 | 平衡方式 | 备注 | 汇编 | | —- | —- | —- | —- | —- | —- | | _cdecl | C语言的调用约定
(VS默认) | 参数从右往左传 | 调用函数者平衡
(add esp ,xx)数字
栈内是ret n
栈外是 add esp 8 | 调用者平衡,栈外平衡,函数外平衡等价
push 2
push 1
call
add esp,8| | | _stdcall | 标准调用约定
(WINAPI) | 参数从右往左传,通过push传参 | 函数自己平衡
(ret xx)retn 数字| API函数 都是stdcall
push 2
push 1
call| | | _fastcall | 快速调用约定,c++
| 前两个参数依次通过ecx,edx传递,后续参数依次从右往左入栈 | 函数自己平衡
(ret xx)retn 数字 | 函数自己平衡,栈内平衡,函数内平衡 被调用者平衡 等价
push 5
push 4
push 3
mov edx,2
mov ecx,1
call | | | _thiscall | 对象调用约定,
(C++ 成员函数的调用约定) | 参数从右往左,会多传一个参数,是对象的地址,叫做this指针,通过ecx传递的 | 函数自己平衡
(ret xx)retn 数字 | 特指C++ 中成员函数
的调用
push 2
push 1
lea ecx,[addr]
call | | | x64call | 64位程序调用约定 | X64 delphi
使用方式,fastcall 参数压栈方式是x64前四个参数使用寄存器 其余使用push 从右到左 | ecx、edx、R8、R9 | mov r9d,4
mov r8d,3
mov edx,2
mov ecx,1
call
| | | delphi call | delphi调用约定 | delphi call: 前三个参数使用 1.eax,2.edx 3.ecx 传递,后面使用栈传递 || mov ecx ,3
mov edx,2
mov eax,1
call ||
函数栈帧布局
其中的第三条指令可以被替换为
lea esp, [esp-0x4] # mov esp, esp-0x4 add esp, 0xfffffff0 push eax
通常情况下会使用下列指令【收回】栈帧
add esp, 0x4 # cmp esp, ebp checkesp mov esp, ebp pop ebp
- **局部变量和参数的分析**
![image.png](https://cdn.nlark.com/yuque/0/2021/png/22743586/1637377079376-82f4a1ba-80ff-4323-b00a-8fdfe6b0ab5d.png#clientId=ud7522eb6-9bf4-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=514&id=u28ae8f51&margin=%5Bobject%20Object%5D&name=image.png&originHeight=841&originWidth=820&originalType=url&ratio=1&rotation=0&showTitle=false&size=42048&status=done&style=none&taskId=ue1ec3f84-549d-4f50-a5ac-5925c0f153f&title=&width=501)
- **栈的布局结构如下**
- 在一个函数中,
- ebp保存的是调用方函数的ebp
- ebp+4保存的是当前函数的返回值 **eip**
- ebp-n 到 esp中保存的是所有的局部变量
- ebp+8开始的位置保存的是当前函数的参数。
![image.png](https://cdn.nlark.com/yuque/0/2021/png/22743586/1637377080358-80e8d03f-bf96-4145-b2c5-35bbf13b0718.png#clientId=ud7522eb6-9bf4-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=474&id=u32f9fb8b&margin=%5Bobject%20Object%5D&name=image.png&originHeight=881&originWidth=627&originalType=url&ratio=1&rotation=0&showTitle=false&size=255444&status=done&style=none&taskId=uc035b1a0-93e0-4ad2-bed1-fa002b7fc1b&title=&width=336.9930725097656)
<a name="WK4Fd"></a>
### 常见函数的识别
<a name="QUjHE"></a>
#### __security_cookie: GS检查,用于检查代码产生的缓冲区溢出
```cpp
# 初始化安全cookie的地方,通常位于程序开始
mov eax,dword ptr [__security_cookie (0C3A004h)]
xor eax,ebp
mov dword ptr [ebp-4],eax
# 校验安全cookie,通常位于程序结尾
mov ecx,dword ptr [ebp-4]
xor ecx,ebp
call @__security_check_cookie@4 (0C311DBh)
CheckEsp:检查堆栈是否平衡,通常位于函数的结尾
# 通常调用约定不正确,会检查失败
add esp, n
cmp ebp,esp
call __RTC_CheckEsp (0C31212h)
CheckStackVars:用于检查数组是否越界,通常在数组操作后被调用
mov ecx,ebp
push eax
lea edx,ds:[0C3177Ch]
call @_RTC_CheckStackVars@8 (0C31235h)
00000000 00000000 00000000 00000000 cccccccc
程序特征的识别
- E8 Call Offset(调用普通函数)(VS)
- FF 15 Call Dword ptr [ 地址 ] (调用IAT)(VS)
VC 6.0 & 易语言
- **OEP特征**
- **入口第一个CALL是调用**_**GetVersion**_
- **Release版:sub esp, 0x58**
- **Debug版: add esp,-0x5c**
- **二进制特征:55 8B EC 6A FF 68 ?? ?? ?? ?? 68 ?? ?? ?? ?? 64 A1 00 00 00 00 50 64 89 25 00 00 00 00**
- **易语言在查找字符串(ASCII)一般有花指令,使用OD插件 EJunk Code 去掉花指令**
- **区段信息、链接器版本 6.0**
Delphi 程序
- **OEP特征**
1. **连续的5个call,紧跟着一串0,第一个call内调用了**_**GetModuleHandleA**_
2. **二进制特征:55 8B EC 83 C4 F0 B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? A1 ?? ?? ?? ?? 8B 00**
3. **IAT函数的调用通常都是 E8 跳转表 + FF25 IAT 地址**
4. **查找按钮事件 右键--查找---查找二进制字符串740E8BD38B83????????FF93????????**
5. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/22743586/1637377081155-23395c2d-d702-428d-b357-097a532ea042.png#clientId=ud7522eb6-9bf4-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=445&id=u57aa8b67&margin=%5Bobject%20Object%5D&name=image.png&originHeight=479&originWidth=901&originalType=url&ratio=1&rotation=0&showTitle=false&size=133246&status=done&style=none&taskId=uff77decd-af7d-4fc5-9636-f6cc65f5339&title=&width=837.3333740234375)
- **链接器版本 2.25**
- **区段信息**
Borland C++ 程序
- **OEP特征**
1. **OEP是一个短跳+字符”fb:C++HOOK”**
2. **第一个调用的函数:GetModuleHandleA**
3. **二进制特征:EB 10 66 62 3A 43 2B 2B 48 4F 4F 4B**
4. **IAT函数的调用通常都是 E8 跳转表 + FF25 IAT 地址**
链接器版本 5.0
- **区段信息**
汇编程序
- **OEP的特征**:_**文件体积小**_,入口点直接就是逻辑代码;由于其编写代码不包括C运行库或者其他的一些库,OEP完全由程序员自己指定,那么特征不明显。
- **链接器版本**:一般链接器版本5.12 或者 2.18
C#程序
- **专门的工具——DnSpy、ILSpy**
VB( VB5、VB6 )
- **专门的工具——VB Decomplier**
- **OEP特征**
1. Push参数之后,调用VB的Dll(msvbvm50 或 msvbvm60
2. OEP 上面有一片调用IAT的代码(**FF25 IAT 地址**)
- **区段信息、链接器版本**
C\C++ 程序(Visual Studio)
- **OEP特征**
- **Debug版本( JMP -> 1Call +2Call+3Call+4Call ):先跳转到某一个地址,地址代码有两个CALL。**
- **第一个是初始化安全Cookie**
- **第二个C运行库函数,内有Main函数或者Winmain函数**
- **Release版本( Call + JMP )**
- **C++的按钮事件采用查找SUB EAX,0A**
- **区段信息、链接器版本 12.0**
- **VS各链接器版本**
数据结构逆向
vector:内部实现为动态数组(初始化10h字节)
list:内部实现为双向循环链表(初始化0Ch字节)
map: 内部的实现是红黑树(平衡二叉树)初始化 (0Ch)字节
string:是一个类对象(初始化1Ch个字节)
struct stringStruct
{
stringStruct *pSelf; //指向自己的指针
typedef union //联合体
{ //保存指向的字符或其指针
char szString[16]; //字符最大盛放16字节
char *pString; //超出的转为使用指针
}MyUnion; //该联合体栈10h个字节
int nSize; //元素长度
int nMaxSize; //元素的最大长度
}
- 常见的逆向分析方法
- 字符串搜索
- API 断点
- 特征码查找(MFC)
- 动静结合
- 源码对比(Demo)
- …………
2. 工具的使用
- 查壳工具
- 用于查看和确定目标的程序版本以及是否被加壳,原理是特征码识别
- PEID、ExeInfo、DetectitEasy
- PE查看工具
- 可用于解析目标程序的PE结构,包括PE头和区段、数据目录表等
- LordPE、PEID、ExeInfo、DetectitEasy、010Editor
动态调试工具
- x64Dbg、OllyDbg、windbg、immunity debugger(方便调试漏洞)、IDA
使用 OllyDbg
- 支持的调试方式
- DLL调试,多线程调试,附加调试,拖拽调试,菜单打开等
- 支持的断点
- 软件断点,硬件断点,条件断点,消息断点,记录断点,内存断点
- 设置消息断点的步骤:
- 怎样找到窗口回调?
- RegisterClass(Ex)、CreateDialog、DialogBoxParam
- 分析模块->右键假定参数->WinProc->断点->在WinProc上设置消息断点
- 怎样找到窗口回调?
- 支持的调试方式
OD各窗口 (Alt+打开窗口)
- L : log 保存日志信息
- E :程序的所有模块的信息(加载基址,大小,OEP,路径)
- M :程序的内存映射视图
- T :线程信息
- W :窗口信息
- H :句柄表
- C :CPU窗口(反汇编窗口)
- / :补丁信息
- K :调用堆栈
- B :软件断点列表
- R :显示参考(数据引用等)
- … : RUN跟踪
- S : 源码显示窗口
OD常用的快捷键
- F8:单步步过
- F7:单步步入
- F9:运行
- Ctrl+F9 :运行到返回(ret指令位置)
- F2 :设置断点
- F4:运行到光标处:
- F12:停止程序执行
- 向前查看: -号
- 向后查看: +号
- 设置注释: ; 分号
- 增加标签:;冒号
- Ctrl+A :分析代码(函数,参数…)
- Ctrl+G : 跳转到
- Ctrl+X : 复制地址
- Shift+X:复制二进制数据
- Ctrl+E :修改内存
- Ctrl+B :二进制搜索 特征码
- Shift+B :编辑二进制数据
- Ctrl+F :指令搜索(单条指令)
- Ctrl+S :指令搜索(多条指令)
- Ctrl+F12: 重启调试
- Enter : 进入函数(跟踪指令)
静态调试工具
- DA 6.8 /7.0: 支持不同平台不同架构程序的调试
- IDA的反汇编引擎采用递归下降的解析方式
- 导航条:
- 蓝色 表示常规的指令函数
- 黑色 节与节之间的间隙
- 银白色 数据内容
- 粉色 表示外部导入符号
- 暗黄色 表示ida未识别的内容
- IDA 主要窗口:
- IDA View三种反汇编视图:文本视图、图表视图、路径视图
- Hex View 十六进制窗口
- Imports 导入函数窗口
- Structures 结构体窗口
- Exports 导出函数窗口
- Enums 枚举窗口
- Strings 字符串窗口 Shift+F12
- Signature签名窗口 Shift+F5
- Name 名称窗口 Shift+F4
- 箭头:代表着导入的名称(导入表中的)
- f:表示这个符号是一个函数
- d:这个是一个数据的名称
- a:代表这是一个字符串的符号
- IDA的常用快捷键:
- ctrl + x或 X 交叉引用(数据、函数)
- ctrl + f: 窗口搜索数据
- ESC:返回上一层
- F2:修改内存数据
- F5::查看伪代码
- a: 转ASCII 字符串
- h: 十六进制十进制的转换
- y: 更改参数或函数的类型
- n: 修改数据或函数的名字
- r: 修改成char型
- c: 将数据转成代码
- p: 将代码转成函数
- g:搜索地址和符号
- 空格键:切换文本视图与图表视图
- 冒号键::常规注释
- 分号键;:可重复注释
- Alt+M:添加标签
- Ctrl+M:查看标签
- Ctrl+S:查看段的信息
- Alt+T:搜索文本
- Alt+B:搜索十六进制
- 代码数据切换
- C—>代码/D—>数据/A—>ascii字符串/U—>解析成未定义的内容
- 伪C代码窗口:
- 右键
- comment-注释伪c代码。
- copy to -assembly-把伪c代码复制到反汇编窗口的汇编代码。