作者论坛账号:爱飞的猫
针对易语言静态编译的代码进行轻微混淆处理,避免被插件一键识别部分关键函数。处理过的内容参考下方。
注意该轻微膨胀/混淆只能用来对抗现有的“一键识别”工具,不能和加密壳的效果比。
如果你能拿到这类工具的源码缝缝补补,应该也能让它重新识别。
对已知的部分特征进行魔改,大部分时候都是对简单操作进行膨胀,然后开辟新的内存空间跳转过去。
因为生成代码计算绝对地址比较麻烦,所以依赖 CALL 指令自动入栈的返回地址来计算正确跳转位置。
只处理了一小部分特征。手动整起来太麻烦了,感觉不如利用特征码自动标记然后上加密壳批量处理了。
对抗 查找易语言按钮事件 插件
对抗 EWnd v0.2 插件的一键分析
对抗 EWnd Ultimate 插件的一键分析
对抗 E-Debug 程序/插件的一键分析
对抗 E-Decompiler 插件的一键分析
对抗 易语言逆向分析助手 的窗体信息分析
对抗易语言初始化入口识别(cld; fninit; call xxxx
)
对抗控件处理事件识别(重写了个简单的,混淆程度不高 call dword[ebp - 4]
)
处理了找到的一些乱七八糟的特征…
首先确保安装有 VS 2022、CMake、Git for Windows 这三个程序。CMake 安装时需要选择将 cmake.exe
注册到系统。
复制代码 隐藏代码:: 克隆仓库 git clone https://github.com/FlyingRainyCats/ELangPatcher.git cd ELangPatcher :: 更新子模组 git submodule update --init --recursive :: 开始构建 cmake -Bcmake-build-vs2022 -G "Visual Studio 17 2022" -A Win32 cmake --build cmake-build-vs2022 --config Release
将可执行文件拖放到 ELangPatcher.exe
即可处理。
可以指定额外参数:
--suffix _p
指定新的后缀,写出到新的文件。
-b, --backup
是否备份原始文件,默认启用。
--fake-stub
插入假的特征码内容到文件,默认启用。
-h, --help
查看帮助信息
示例输出:
复制代码 隐藏代码* (2). "M:\Programs\E_5.1\tools\ELangPatcher.exe" --suffix _ -- "M:\Projects\e-AntiWnd\测试3_5.1_静态编译.exe" ELang Patcher v0.1 by FlyingRainyCats (爱飞的猫 @52pojie.cn) INFO: processing: M:\Projects\e-AntiWnd\测试3_5.1_静态编译.exe INFO: [PatchDllFunctionInvokeCall] found (offset=0x0001ac80, call_delta=0xffffffda) INFO: [PatchEWndV02] found (offset=0x00013a40, ecx=0x004b3658, call_delta=0xffffc787) INFO: [PatchEWndUltimate] found (offset=0x00038adf, data=0x004816a0, wnd_data_offset=0x000816a0) - stub added: 0x0048074d (file offset: 0008074d) INFO: [PatchWndEventHandlerMain] found (fn_end=0001adb6, offset=0x0001ad10, inst=0x0001ad3a(p: 0x0041ad3a), delta=0xffff9ab1) INFO: [PatchKernelInvokeCall#0] found (offset=0x0001ac90, len=003b, replace_len=002c) INFO: [PatchKernelInvokeCall#1] found (offset=0x0001acd0, len=0031, replace_len=0025) INFO: [ELibInvokeCall#0] found (offset=0x0001ac60, args=[1], ecx=0x004b3658, call_delta=0xffff7dbf) INFO: [ELibInvokeCall#1] found (offset=0x0001b0a0, args=[1], ecx=0x004b3658, call_delta=0xffff718f) INFO: [LoadInitWindow#0] found (offset=0x0001b020, args=[4], ecx=0x004b3658, call_delta=0xffff8b33) INFO: [UnknownCtrlRelated#0] found (offset=0x0001b040, args=[6], ecx=0x004b3658, call_delta=0xffff9a4b) INFO: [PatchLoadWndCall] found (offset=0x000015cb, ebx=0x004017e0, call_delta=0x0000012a) INFO: [PatchLoadWndCall] found (offset=0x00001007, ebx=0x004017e0, call_delta=0x000006f7) INFO: [PatchLoadWndCall] found (offset=0x000016aa, ebx=0x004017e0, call_delta=0x00000054) INFO: [PatchSuspiciousCallWithParam] found (offset=0x000012f1, push_value=0x52010048, call_delta=0x00000457) INFO: [PatchSuspiciousCallWithParam] found (offset=0x000013a1, push_value=0x52010048, call_delta=0x000003a7) INFO: [PatchSuspiciousCallWithParam] found (offset=0x00001449, push_value=0x00000006, call_delta=0x000002ed) INFO: [PatchSuspiciousCallWithParam] found (offset=0x000014d9, push_value=0x00000006, call_delta=0x0000025d) INFO: [PatchSuspiciousCallWithParam] found (offset=0x00001584, push_value=0x00000006, call_delta=0x000001b2) INFO: [PatchSuspiciousCallWithParam] found (offset=0x00001626, push_value=0x00000006, call_delta=0x00000110) INFO: [PatchSuspiciousCallWithParam] found (offset=0x00001686, push_value=0x52010048, call_delta=0x000000aa) INFO: [PatchSuspiciousCallWithParam] found (offset=0x00001707, push_value=0x52010048, call_delta=0x00000011) INFO: [PatchELangLoaderInitStub] found (offset=0x000016ed) INFO: [MiscAddFakeEWndStub] add stub (len=140 bytes)
打开易语言目录;
打开该目录下的 tools
目录;
将 ELangPatcher.exe
放入该目录;
打开 link.ini
配置文件;
找到结尾的 post_link_action
区域,并添加新的操作。
静态编译后自动处理,参考添加 post_link_action1="$(E_TOOLS)\ELangPatcher.exe" $(TARGET)
;
如果有自动加壳,你需要调整序号,让 ELangPatcher.exe
先执行;
混淆 cld/fninit 特征 (LibELangPatch/ELangInitFnGen.cpp
):
本质上就是抽取 cld; fninit; call xxxx
这串代码到别的地方,并插入随机垃圾代码避免现有特征码定位:
复制代码 隐藏代码/** * 0040116D | FC | cld <-- addr start * 0040116E | DBE3 | fninit * 00401170 | E8 ECFFFFFF | call exe.401161 <-- call_delta = 0x0xFFFFFFEC * @param call_delta This can be `{}` if the call is empty. * @return */ std::vector<uint8_t> GenerateELangLoaderInit(std::optional<uint32_t> call_delta); class ELangLoaderInitGen : public CodeGenHelper { public: explicit ELangLoaderInitGen(std::optional<uint32_t> call_delta) { auto regs = shuffled<Reg32>({eax, edx, ecx}); fillWithJunkSlideInst(rand_int(1, 5), regs); shuffle_exec({ [&]() { cld(); genJunk(regs); }, [&]() { fninit(); genJunk(regs); }, }); fillWithJunkSlideInst(rand_int(1, 5), regs); bool use_ret_trick{false}; if (call_delta) { use_ret_trick = next_bool(); auto reg_ret_addr = pop_last_item(regs); mov(reg_ret_addr, dword[esp]); genJunk(regs); if (use_ret_trick) { add(dword[esp], 3); genJunk(regs); } auto delta_signed = static_cast<int32_t>(*call_delta); if (delta_signed > 0) { add(reg_ret_addr, delta_signed); } else { sub(reg_ret_addr, -delta_signed); } genJunk(regs); if (use_ret_trick) { jmp(reg_ret_addr); } else { call(reg_ret_addr); } } regs = shuffled<Reg32>({eax, edx, ecx}); if (!use_ret_trick) { genJunk(regs); add(dword[esp], 3); } genJunk(regs); ret(); std::vector<uint8_t> junk(rand_int(4, 10)); std::generate(junk.begin(), junk.end(), mt_); db(junk.data(), junk.size()); } }; std::vector<uint8_t> GenerateELangLoaderInit(std::optional<uint32_t> call_delta) { return ELangLoaderInitGen{call_delta}.vec(); }
扩充 .text
段,若无剩余空间则建立新的 .txt2
段储存代码 (src/PEParser.h
):
复制代码 隐藏代码 inline uint8_t *ExpandTextSection(uint32_t size) { auto p_nt_header = INNER_PIMAGE_NT_HEADERS((uint8_t *) (exe_data_.data()) + PIMAGE_DOS_HEADER(exe_data_.data())->e_lfanew); auto p_file_header = &p_nt_header->FileHeader; auto section_count = std::size_t(p_file_header->NumberOfSections); auto section = (PIMAGE_SECTION_HEADER) ((uint8_t *) (p_nt_header) + offsetof(INNER_IMAGE_NT_HEADERS, OptionalHeader) + p_nt_header->FileHeader.SizeOfOptionalHeader); auto &image_size = GetNtOptionalHeader()->SizeOfImage; for (std::size_t i = 0; i < section_count; i++) { if (strcmp((char *) section->Name, ".text") == 0) { if (section->Misc.VirtualSize + size < section->SizeOfRawData) { auto offset = section->Misc.VirtualSize; section->Misc.VirtualSize += size; return &exe_data_.at(section->PointerToRawData + offset); } } else if (strcmp((char *) section->Name, ".txt2") == 0) { exe_data_.resize(exe_data_.size() + size); auto offset = section->SizeOfRawData; section->SizeOfRawData += size; image_size -= section->Misc.VirtualSize; section->Misc.VirtualSize = helper::round_up_to_section_size(section->SizeOfRawData); image_size += section->Misc.VirtualSize; return &exe_data_.at(section->PointerToRawData + offset); } section++; } auto last_section = §ion[-1]; p_file_header->NumberOfSections++; memset(section, 0, sizeof(*section)); memcpy(section->Name, ".txt2", 6); section->PointerToRawData = exe_data_.size(); section->SizeOfRawData = size; section->Misc.VirtualSize = helper::round_up_to_section_size(size); image_size = helper::round_up_to_section_size(image_size) + section->Misc.VirtualSize; section->VirtualAddress = helper::round_up_to_section_size(last_section->VirtualAddress + last_section->Misc.VirtualSize); section->Characteristics = IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_CODE; exe_data_.resize(exe_data_.size() + size); return &exe_data_.at(exe_data_.size() - size); }
程序:
ELangPatcher-0.1.1.zip 见左下角论坛原文
源码:https://github.com/FlyingRainyCats/ELangPatcher |
ELangPatcher-0.1.1-src.zip 见左下角论坛原文
百度网盘存档/测试程序 https://pan.baidu.com/s/1Jlbqt8K1fIG6PPbfrrEI\_A?pwd=q565
v0.1.1: 修正 GenerateVArgsProxyCode
的代码生成(如 取余数
传参),感谢 谁的坏叔叔
报告。
v0.1: 初版发布
代码写得比较乱,大概率不会有后续更新了…
拿 Xbyak
生成字节码,调试错误的时候也是磕磕碰碰。因为都是手动插入的垃圾指令,早期写得那些代码生成的混淆“相对温和”。后期整得有点走火入魔,也尝试了下隐藏常数。
一开始就想着对抗下 “一键 PUSH”,这个目的倒是达到了。不过其它的特征… 手动根本就处理不完。
在 .text
外的内容如果有匹配上特征码也会尝试进行魔改。读者有兴趣可以限制特征码检索为 .text
区段的代码。
不过,这些混淆/膨胀的手段在 IDA 的帮助下分析起来倒是不怎么费劲就是…
易语言的编译顺序太“稳定”了,可以通过定位目标函数附近的函数来快速定位。
没处理过特征的函数依然可以手动检索特征码找到。
再做下去感觉需要自动化识别函数、反编译、然后随机混淆/膨胀了。
要考虑的东西太多了,不适合我。
fjqisba 老师的易语言逆向专栏
易语言程序分析笔记 - 看雪/PlaneJun
-官方论坛
👆👆👆
公众号设置“星标”,您不会错过新的消息通知
如开放注册、精华文章和周边活动等公告