识别程序的特征
如何识别程序的特征
使用PE查看工具查看 链接器版本
使用PE查看工具查看 区段信息
查看目标程序的 二进制特征
BC++ 程序(Borland C++)
程序的二进制特征:
1 | EB 10 JMP SHORT BC6.0040130E |
BC++ 编写的程序,IAT函数的调用通常都是 E8 跳转表 + FF25 IAT 地址
链接器版本:
区段特征:
Delphi 程序
连接器版本:2.25
区段特征:
二进制特征:连续的 5 个 call 后面紧跟着一堆 0
1 | 55 PUSH EBP |
VB6 程序
链接器版本
区段特征
二进制特征(VB程序一般直接使用工具进行反编译)
易语言、VC6
二进制特征,两个的特征基本信息
1 | 55 PUSH EBP |
连接器特征
区段特征
汇编程序
汇编程序的链接器版本和区段特征都是不固定的,也没有明确的二进制特征。可以通过查看文件的大小来猜测是不是一个汇编程序。程序的 OEP 部分通常直接就是逻辑代码,也可以作为标识。
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; |
常量信息可能被保存在常量数据区和代码区
字符串常量的初始化
1 | // 字符串数组 |
对于一个字符串,初始化通常是: 四字节拷贝 + 不足四字节拷贝 + 填充剩余的空间为 0
如果初始化数据较多,第一步和第二步会简化成一个串操作进行赋值
对于一个没有指定大小的字符串初始化操作,就没有填充剩余空间的步骤
指针和引用
指针和引用在汇编层面的实现完全一致
1 | int* pnumber = &number; |
分析对象
构造函数的分析
构造函数会使用 ecx 传递对象的首地址(this),构造函数的返回值是 this
一个存在继承关系且有虚函数的构造函数 = 构造父类\成员 + 初始化虚表指针 + 用户代码 + 返回值
析构函数的分析
析构函数中没有设置返回值。
一个存在继承关系且有虚函数的析构函数 = 重置虚表指针 + 用户代码 + 析构父类
成员函数和数据成员
成员函数的调用会使用 ecx 传递 this 指针
数据成员的使用是通过 this 加上一个偏移得到
虚函数
并不是所有的虚函数调用都会有动态联编
对系统代码的识别
初始化安全 cookie
1 | 002A171E mov eax,dword ptr [__security_cookie (02AA004h)] |
检查一段代码是否是用户代码
1 | 002A1728 mov ecx,offset _2A6D85CB_ |
检查数组是否越界
1 | 002A17EE lea edx,ds:[2A181Ch] |
检查当前的程序是否被缓冲区溢出攻击
1 | 002A17FE mov ecx,dword ptr [ebp-4] |
检查堆栈是否平衡
1 | 002A1808 add esp,220h |