本文为看雪论坛优秀文章
看雪论坛作者ID:奋进的小杨
前言
上一篇IDA静态逆向分析模型原理透析比较受到大家的欢迎。所以这一篇笔记,是对上一篇的补充。
IDA是用来做手工分析的辅助工具。它的反汇编时间,与程序代码的大小,有直接的关系。大体,我们可以将这个过程分为两个阶段:
(1) 代码与数据的分开,标记各个函数和符号并分析参数调用、参数栈、局部变量栈、跳转等指令的关系,并分析数据结构,自动生成流程图和模块调用关系。
一、查看PE文件
二、分析程序结构
三、识别库函数
四、识别数据结构
用户可以手动加载头文件添加数据结构,同时也可以自定义数据结构。
五、代码分析
六、函数识别
七、对C++程序的完整分析实验环境
测试对象:AltStreamDump v1.05
1. 去除软件保护
通过exeinfope,查看结果如下:
2. 分析类型
3. 识别库函数
4. 查找程序的入口
这里发现text:0x401914 call sub_401914符合要求,并且该函数调用之前使用了三个压栈指令。
int wmain(int argc,wchar_t argv[],wchar_t envp[])
这三个参数,我来说明一下。分别是命令行参数,命令行个数,环境变量。
5. 分析函数属性
由于太多,不方便截屏,故代码呈上。
int __cdecl wmain(signed int a1, int a2)
{
signed int v2; // edi@1
wchar_t *v3; // eax@2
HRSRC v4; // eax@9
HGLOBAL v5; // eax@10
const char *v6; // eax@11
HMODULE hLibModule; // [sp+Ch] [bp-454h]@1
wchar_t *Str; // [sp+10h] [bp-450h]@2
wchar_t *Str1; // [sp+14h] [bp-44Ch]@2
int (__stdcall **v11)(int); // [sp+18h] [bp-448h]@1
int v12; // [sp+1Ch] [bp-444h]@1
int v13; // [sp+20h] [bp-440h]@14
wchar_t v14; // [sp+24h] [bp-43Ch]@1
int v15; // [sp+230h] [bp-230h]@1
int v16; // [sp+234h] [bp-22Ch]@1
int v17; // [sp+238h] [bp-228h]@14
int v18; // [sp+23Ch] [bp-224h]@14
int v19; // [sp+240h] [bp-220h]@14
int v20; // [sp+244h] [bp-21Ch]@14
int v21; // [sp+248h] [bp-218h]@14
int v22; // [sp+24Ch] [bp-214h]@14
WCHAR Buffer; // [sp+250h] [bp-210h]@1
char Dst; // [sp+252h] [bp-20Eh]@1
hConsoleOutput = GetStdHandle(0xFFFFFFF5);
v14 = 0;
v16 = 0;
v12 = 0;
v11 = &off_4022D8;
Buffer = 0;
memset(&Dst, 0, 0x208u);
GetCurrentDirectoryW(0x104u, &Buffer);
v15 = 0;
sub_4013C4(&Buffer);
v2 = a1;
for ( hLibModule = (HMODULE)1; (signed int)hLibModule < v2 - 1; hLibModule = (HMODULE)((char *)hLibModule + 1) )
{
v3 = *(wchar_t **)(4 * (_DWORD)hLibModule + a2 + 4);
Str1 = *(wchar_t **)(4 * (_DWORD)hLibModule + a2);
Str = v3;
if ( !wcscmp(Str1, L"-d") )
{
v15 = 1;
v16 = wtoi(Str);
}
if ( !wcscmp(Str1, L"-f") )
{
sub_4013C4(Str);
v2 = a1;
}
}
if ( v2 > 1 && !wcscmp(*(const wchar_t **)(a2 + 4), L"-h") )
{
v4 = FindResourceW(0, (LPCWSTR)0x65, L"BIN");
if ( v4 && (v5 = LoadResource(0, v4)) != 0 )
v6 = (const char *)LockResource(v5);
else
v6 = 0;
printf(v6);
}
else
{
hLibModule = 0;
sub_401B0D();
v13 = 0;
v17 = 0;
v18 = 0;
v19 = 0;
v20 = 0;
v21 = 0;
v22 = 0;
sub_401004(&v14, 0);
sub_401848();
wprintf(L"rnrnType AltStreamDump -h to get more information about this tool.rn");
if ( hLibModule )
FreeLibrary(hLibModule);
}
return 0;
}
6. 分析局部变量
可见局部栈变量使用了不少于454h字节的空间,因此需要从函数头部开始分析栈使用情况判断使用了哪些变量及这些变量的作用域。
7. 分析函数功能
GetCurrentProcess、LoadLibraryW“advapi32.dll”、OpenProcessToken 、LookupPrivilegeValueW、AdjustTokenPrivileges、Closehandle
通过在Windows官网API查询,可以知道,这是系统的提权。
8. 分析函数算法
9. 分析类
我们可以发现该函数申请了很大的局部变量空间,对于类成员函数调用的线索,可以看到该函数最后在text:00401AC0处有一条指令为:
lea esi,[esp+460h+var_448]
然后又调用子函数sub_401848,而且在该子函数中,直接使用了esi,因此认定这个函数为成员函数(所有使用了this指针的函数,都可以看成是成员函数)。
同时关注之前esi和var_448的操作情况,将var_448作为栈上存储类的对象空间开始处,对于其结束位置,需要我们从两个方面推测,一方面是wmain函数在该栈上该类空间起始位置后的第一个其他变量所在位置,该对象在栈上的结束位置不可能超过这个值。
另一方面,esi是this指针这个信息从函数列表中所有的子函数中进行搜索,查看对于this指针的最大偏移数,由于对象并没有赋予虚表指针的情况,因此最大的偏移数就可以假定是临近最后的成员变量位置。
根据这种方法综合分析,得到程序中的两个自命名类,一个是用于控制显示部分的MainClass,另一个用于文件查找的FindFileClass。这样就根据this指针确定所有成员函数和类成员。
另外调用第一个成员函数之前存在对对象成员变量域的赋值操作,这种操作极有可能是类构造函数采用了优化内联的形式存在于wmain函数中。对于是否为构造函数,可以通过该类每次出现于内存中是否都执行了构造函数这个本质进行验证,如果不是则认为它是一般的成员函数,对于析构函数同理。
分析得到的两个简单的类结构,如下:
class FindFileClass
{
public:
FindFileClass();
void GenSearchString(LPCNSIN Path);
Bool EnumNextFile();
void ReleaseFile();
Public:
HANDLE hFindFile;
WCHAR LastFileName[324];
WCHAR CurrentFileName[324];
WCHAR CurrentFolderPath[324];
}
class MainClass{
public:
MainClass();
void GoSearch();
private:
void ShowStaticinfo();
}
10. 修正函数的属性
11. 分析异常处理
12. 分析RTTI信息
八、重建开发文档
使用说明:不需要任何安装过程或附加dll文件,可以直接打开命令提示符就可以运行该程序。程序默认显示当前目录的文件数据流。
可以通过-f 和-d 命令行参数查看其他文件夹的文件数据流。
命令行参数:
h:显示命令行帮助。
f:指定要搜索的目录。
d:用于指定要搜索的父目录深度(0=不搜索子目录,1=搜索1级子目录。)
看雪ID:奋进的小杨
https://bbs.pediy.com/user-home-910385.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!
本文始发于微信公众号(看雪学院):IDA静态逆向分析模型原理的补充
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论