0x12 逆向

1. 汇编与逆向


  • 函数调用约定 | 调用约定 | 约定名称 | 传参方式 | 平衡方式 | 备注 | 汇编 | | —- | —- | —- | —- | —- | —- | | _cdecl | C语言的调用约定
    (VS默认) | 参数从右往左传 | 调用函数者平衡
    (add esp ,xx)数字
    栈内是ret n
    栈外是 add esp 8 | 调用者平衡,栈外平衡,函数外平衡等价
    push 2
    push 1
    call
    add esp,8

    | image.png | | _stdcall | 标准调用约定
    (WINAPI) | 参数从右往左传,通过push传参 | 函数自己平衡
    (ret xx)retn 数字

    | API函数 都是stdcall
    push 2
    push 1
    call

    | image.png | | _fastcall | 快速调用约定,c++

    | 前两个参数依次通过ecx,edx传递,后续参数依次从右往左入栈 | 函数自己平衡
    (ret xx)retn 数字 | 函数自己平衡,栈内平衡,函数内平衡 被调用者平衡 等价

    push 5
    push 4
    push 3
    mov edx,2
    mov ecx,1
    call | image.png | | _thiscall | 对象调用约定,
    (C++ 成员函数的调用约定) | 参数从右往左,会多传一个参数,是对象的地址,叫做this指针,通过ecx传递的 | 函数自己平衡
    (ret xx)retn 数字 | 特指C++ 中成员函数
    的调用

    push 2
    push 1
    lea ecx,[addr]
    call | image.png | | x64call | 64位程序调用约定 | X64 delphi
    使用方式,fastcall 参数压栈方式是x64前四个参数使用寄存器 其余使用push 从右到左 | ecx、edx、R8、R9 | mov r9d,4
    mov r8d,3
    mov edx,2
    mov ecx,1
    call
    | image.png | | delphi call | delphi调用约定 | delphi call: 前三个参数使用 1.eax,2.edx 3.ecx 传递,后面使用栈传递 |

    | mov ecx ,3
    mov edx,2
    mov eax,1
    call |

    |

函数栈帧布局

  • 开辟栈帧和回收栈帧 ```cpp

    通常情况下会使用下列指令【开辟】栈帧

    push ebp mov ebp, esp sub esp, 0x4

其中的第三条指令可以被替换为

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

  1. - **局部变量和参数的分析**
  2. ![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)
  3. - **栈的布局结构如下**
  4. - 在一个函数中,
  5. - ebp保存的是调用方函数的ebp
  6. - ebp+4保存的是当前函数的返回值 **eip**
  7. - ebp-n esp中保存的是所有的局部变量
  8. - ebp+8开始的位置保存的是当前函数的参数。
  9. ![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)
  10. <a name="WK4Fd"></a>
  11. ### 常见函数的识别
  12. <a name="QUjHE"></a>
  13. #### __security_cookie: GS检查,用于检查代码产生的缓冲区溢出
  14. ```cpp
  15. # 初始化安全cookie的地方,通常位于程序开始
  16. mov eax,dword ptr [__security_cookie (0C3A004h)]
  17. xor eax,ebp
  18. mov dword ptr [ebp-4],eax
  19. # 校验安全cookie,通常位于程序结尾
  20. mov ecx,dword ptr [ebp-4]
  21. xor ecx,ebp
  22. call @__security_check_cookie@4 (0C311DBh)

CheckEsp:检查堆栈是否平衡,通常位于函数的结尾

  1. # 通常调用约定不正确,会检查失败
  2. add esp, n
  3. cmp ebp,esp
  4. call __RTC_CheckEsp (0C31212h)

CheckStackVars:用于检查数组是否越界,通常在数组操作后被调用

  1. mov ecx,ebp
  2. push eax
  3. lea edx,ds:[0C3177Ch]
  4. call @_RTC_CheckStackVars@8 (0C31235h)
  5. 00000000 00000000 00000000 00000000 cccccccc

程序特征的识别

  • E8 Call Offset(调用普通函数)(VS)
  • FF 15 Call Dword ptr [ 地址 ] (调用IAT)(VS)

VC 6.0 & 易语言

  1. - **OEP特征**
  2. - **入口第一个CALL是调用**_**GetVersion**_
  3. - **Release版:sub esp, 0x58**
  4. - **Debug版: add esp,-0x5c**
  5. - **二进制特征:55 8B EC 6A FF 68 ?? ?? ?? ?? 68 ?? ?? ?? ?? 64 A1 00 00 00 00 50 64 89 25 00 00 00 00**
  6. - **易语言在查找字符串(ASCII)一般有花指令,使用OD插件 EJunk Code 去掉花指令**

image.png

  1. - **区段信息、链接器版本 6.0**

image.png

Delphi 程序

  1. - **OEP特征**
  2. 1. **连续的5call,紧跟着一串0,第一个call内调用了**_**GetModuleHandleA**_
  3. 2. **二进制特征:55 8B EC 83 C4 F0 B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? A1 ?? ?? ?? ?? 8B 00**
  4. 3. **IAT函数的调用通常都是 E8 跳转表 + FF25 IAT 地址**
  5. 4. **查找按钮事件 右键--查找---查找二进制字符串740E8BD38B83????????FF93????????**
  6. 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)
  7. - **链接器版本 2.25**

image.png

  1. - **区段信息**

image.png

Borland C++ 程序

  1. - **OEP特征**
  2. 1. **OEP是一个短跳+字符”fb:C++HOOK”**
  3. 2. **第一个调用的函数:GetModuleHandleA**
  4. 3. **二进制特征:EB 10 66 62 3A 43 2B 2B 48 4F 4F 4B**
  5. 4. **IAT函数的调用通常都是 E8 跳转表 + FF25 IAT 地址**

image.png

链接器版本 5.0
image.png

  1. - **区段信息**

image.png

汇编程序

  1. - **OEP的特征**:_**文件体积小**_,入口点直接就是逻辑代码;由于其编写代码不包括C运行库或者其他的一些库,OEP完全由程序员自己指定,那么特征不明显。
  2. - **链接器版本**:一般链接器版本5.12 或者 2.18

C#程序

  1. - **专门的工具——DnSpyILSpy**

VB( VB5、VB6 )

  1. - **专门的工具——VB Decomplier**
  2. - **OEP特征**
  3. 1. Push参数之后,调用VBDllmsvbvm50 msvbvm60
  4. 2. OEP 上面有一片调用IAT的代码(**FF25 IAT 地址**)
  5. - **区段信息、链接器版本**

image.png

C\C++ 程序(Visual Studio)

  1. - **OEP特征**
  2. - **Debug版本( JMP -> 1Call +2Call+3Call+4Call ):先跳转到某一个地址,地址代码有两个CALL。**
  3. - **第一个是初始化安全Cookie**
  4. - **第二个C运行库函数,内有Main函数或者Winmain函数**
  5. - **Release版本( Call + JMP )**
  6. - **C++的按钮事件采用查找SUB EAX,0A**

image.png

  1. - **区段信息、链接器版本 12.0**

image.png

  1. - **VS各链接器版本**

image.png

数据结构逆向

vector:内部实现为动态数组(初始化10h字节)

image.png

list:内部实现为双向循环链表(初始化0Ch字节)

image.png

map: 内部的实现是红黑树(平衡二叉树)初始化 (0Ch)字节

image.png

string:是一个类对象(初始化1Ch个字节)

  1. struct stringStruct
  2. {
  3. stringStruct *pSelf; //指向自己的指针
  4. typedef union //联合体
  5. { //保存指向的字符或其指针
  6. char szString[16]; //字符最大盛放16字节
  7. char *pString; //超出的转为使用指针
  8. }MyUnion; //该联合体栈10h个字节
  9. int nSize; //元素长度
  10. int nMaxSize; //元素的最大长度
  11. }
  • 常见的逆向分析方法
    • 字符串搜索
    • 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代码复制到反汇编窗口的汇编代码。