本帖最后由 ostara 于 2023-10-29 12:03 编辑
网上看到一款单机游戏,发现加了一个未知壳,拿来练练手。 一. 简单分析 PE Tools 查看Sections,可以看到3个.text代码段,第一个.text RawSize和Offset都为0,代码可能被压缩,后两个段可能为外壳代码 负责解压真实代码。 x64 DBG 打开程序在OEP断下,OPE位于第三个.text段,查看第一个.text段,发现数据已经被写入到.text段。说明外壳程序在 TLS回调 时已经解压了第一个 .text段的数据并写入,OEP位于第三个.text段可能后续会对程序进行crc 之类的校验以及反调试。 二.寻找OEP 使用ESP定律,在rsp-8的位置下断,一直按F9运行。 最后真实OEP在第一个.text段位置断下。 三.修复IAT 表 使用Scylla 自动搜索IAT。 发现很多无效的地址,并且都位于外壳程序的.text段,说明外壳程序硬编码了大部分的iat 地址(使用外壳程序函数地址代替导入函数地址,外壳程序函数内会计算得到导入函数地址 进行间接跳转)。
我们已经确定了IAT的位置,可以尝试使用Unicorn 跑出真实的导入函数地址。 DWORD64 runAndGetAddr(uc_engine* uc, DWORD64 beginAddr) { DWORD64 r_rip; uc_emu_start(uc, beginAddr, 0, 0, 0); uc_reg_read(uc, UC_X86_REG_RIP, &r_rip); return r_rip; } void dealIat(HANDLE hProc, LPVOID exeMem) { if (g_uc) { DWORD64 startIatAddr = 0x000000141489000; DWORD64 endIatAddr = 0x0000000141489840; DWORD64 tempRead; while (startIatAddr < endIatAddr ) { ReadProcessMemory(hProc, (LPVOID)startIatAddr , &tempRead, 8, NULL); if (tempRead) tempRead = runAndGetAddr(g_uc, tempRead); printf("%p:%p\n", startIatAddr , tempRead); if (tempRead) { WriteProcessMemory(hProc, (LPVOID)startIatAddr , &tempRead, 8, NULL); } startIatAddr += 8; } } }
当从外壳程序执行到导入函数时,因为导入函数没有在unicorn 中map,所以会异常退出,得到导入函数地址。 unicorn 是一个模拟执行软件,给一段汇编可以模拟执行得到输出结果。 https://github.com/unicorn-engine/unicorn 此时在使用Scylla 自动搜索IAT,可以得到完整的IAT。 四. dump 镜像 获取iat之后在Dump和Fix Dump 就可以得到脱壳后的程序了
注:若转载请注明大神论坛来源(本贴地址)与作者信息。
|