笔记:逆向004.zip
练习:各种语言程序.zip
一、识别程序的特征
0. 如何识别程序的特征
- 使用PE查看工具查看 链接器版本
- 使用PE查看工具查看 区段信息
- 查看目标程序的 二进制特征
1. BC++ 程序
- 程序的二进制特征
- BC++ 编写的程序,IAT函数的调用通常都是 E8 跳转表 + FF25 IAT 地址
EB 10 66 62 3A 43 2B 2B 48 4F 4F 4B 90
- 链接器版本
- 区段特征:
2. Delphi 程序
- 连接器版本:2.25
- 区段特征:
- 二进制特征:连续的 5 个 call 后面紧跟着一堆 0
55 8B EC 83 C4 F0 B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? A1 ?? ?? ?? ?? 8B 00
3. VB6 程序
- 链接器版本
- 区段特征
- 二进制特征(VB程序一般直接使用工具进行反编译)
4. 易语言、VC6
二进制特征,两个的特征基本信息
55 8B EC 6A FF 68 ?? ?? ?? ?? 68 ?? ?? ?? ?? 64 A1 00 00 00 00 50 64 89 25 00 00 00 00
连接器特征
- 区段特征
5. 汇编程序
- 汇编程序的链接器版本和区段特征都是不固定的,也没有明确的二进制特征。可以通过查看文件的大小来猜测是不是一个汇编程序。程序的 OEP 部分通常直接就是逻辑代码,也可以作为标识。
6. VS 程序的特征
- 不同版本的VS编写出来的程序,特征是不同的
- 通常 debug 版本的程序入口点可能存在 call + call,而 release 是 call + jmp | VS 版本 | 链接器版本 | | :—-: | :—-: | | VC 6.0 | 6.0 | | VC2003 | 7.0 / 7.1 | | VS2005 | 8.0 | | VS2008 | 9.0 | | VS2010 | 10.0 | | VS2012 | 11.0 | | VS2013 | 12.0 | | VS2015 | 14.0 | | VS2017 | 14.1 | | VS2019 | 14.2 |
二、基本数据类型的识别
1. 常量的识别
const bool bRet = true;
const int nCount = SIZE;
const char* szHello = "Hello 15PB";
const eData data = eData::enum_TYPE_1;
const float fNum = 1.5;
const sData stc = { 1,2.0,'1' };
- 常量信息可能被保存在常量数据区和代码区
2. 字符串常量的初始化
// 字符串数组
char szStr[100] = { "szStr[100] Hello 15PB" };
// 宽字符串数组
wchar_t szWchar[100] = L"szWchar Hello 15PB";
// 普通的字符数组
char szHello[] = "szHello[] Hello 15PB";
- 对于一个字符串,初始化通常是: 四字节拷贝 + 不足四字节拷贝 + 填充剩余的空间为 0
- 如果初始化数据较多,第一步和第二步会简化成一个串操作进行赋值
- 对于一个没有指定大小的字符串初始化操作,就没有填充剩余空间的步骤
3. 指针和引用
- 指针和引用在汇编层面的实现完全一致
int* pnumber = &number;
// 00F21739 lea eax, [number]
// 00F2173C mov dword ptr[pnumber], eax
int& rnumber = number;
// 00F2173F lea eax, [number]
// 00F21742 mov dword ptr[rnumber], eax
三、分析对象
1. 构造函数的分析
- 构造函数会使用 ecx 传递对象的首地址(this),构造函数的返回值是 this
- 一个存在继承关系且有虚函数的构造函数 = 构造父类\成员 + 初始化虚表指针 + 用户代码 + 返回值
2. 析构函数的分析
- 析构函数中没有设置返回值。
- 一个存在继承关系且有虚函数的析构函数 = 重置虚表指针 + 用户代码 + 析构父类
3. 成员函数和数据成员
- 成员函数的调用会使用 ecx 传递 this 指针
- 数据成员的使用是通过 this 加上一个偏移得到
4. 虚函数
- 并不是所有的虚函数调用都会有动态联编
四、对系统代码的识别
1. 初始化安全 cookie:
002A171E mov eax,dword ptr [__security_cookie (02AA004h)]
002A1723 xor eax,ebp
002A1725 mov dword ptr [ebp-4],eax
; 安全 cookie 是一个运行时确定的值,使用当前的 ebp 对它进行加密,并放置到 ebp - 4 的位置,如果被缓冲区溢出攻击了,那么 ebp - 4 绝对会被覆盖,覆盖后的值再次使用 ebp 异或解密就无法恢复之前的数值,导致程序结束
2. 检查一段代码是否是用户代码
002A1728 mov ecx,offset _2A6D85CB_
002A172D call @__CheckForDebuggerJustMyCode@4 (02A1208h)
3. 检查数组是否越界
002A17EE lea edx,ds:[2A181Ch]
002A17F4 call @_RTC_CheckStackVars@8 (02A1235h)
4. 检查当前的程序是否被缓冲区溢出攻击
002A17FE mov ecx,dword ptr [ebp-4]
002A1801 xor ecx,ebp
002A1803 call @__security_check_cookie@4 (02A11DBh)
5. 检查堆栈是否平衡
002A1808 add esp,220h
002A180E cmp ebp,esp
002A1810 call __RTC_CheckEsp (02A1212h)