吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5117|回复: 24
收起左侧

[C&C++ 原创] ELangPatcher - 一个简单的基于特征码的易语言静态编译程序膨胀与混淆工具

  [复制链接]
爱飞的猫 发表于 2024-3-23 07:52
[ 本帖最后由 爱飞的猫 于 2024-3-28 08:35 编辑 ]\n\n

针对易语言静态编译的代码进行轻微混淆处理,避免被插件一键识别部分关键函数。处理过的内容参考下方。

注意该轻微膨胀/混淆只能用来对抗现有的“一键识别”工具,不能和加密壳的效果比。

如果你能拿到这类工具的源码缝缝补补,应该也能让它重新识别。

原理

对已知的部分特征进行魔改,大部分时候都是对简单操作进行膨胀,然后开辟新的内存空间跳转过去。

因为生成代码计算绝对地址比较麻烦,所以依赖 CALL 指令自动入栈的返回地址来计算正确跳转位置。

处理过的特征

只处理了一小部分特征。手动整起来太麻烦了,感觉不如利用特征码自动标记然后上加密壳批量处理了。

  • 对抗 查找易语言按钮事件 插件
  • 对抗 EWnd v0.2 插件的一键分析
  • 对抗 EWnd Ultimate 插件的一键分析
  • 对抗 E-Debug 程序/插件的一键分析
  • 对抗 E-Decompiler 插件的一键分析
  • 对抗 易语言逆向分析助手 的窗体信息分析
  • 对抗易语言初始化入口识别(cld; fninit; call xxxx
  • 对抗控件处理事件识别(重写了个简单的,混淆程度不高 call dword[ebp - 4]
  • 处理了找到的一些乱七八糟的特征…

从源码构建

首先确保安装有 VS 2022CMakeGit 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)

集成到易语言

  1. 打开易语言目录;
  2. 打开该目录下的 tools 目录;
  3. ELangPatcher.exe 放入该目录;
  4. 打开 link.ini 配置文件;
  5. 找到结尾的 post_link_action 区域,并添加新的操作。
    1. 静态编译后自动处理,参考添加 post_link_action1="$(E_TOOLS)\ELangPatcher.exe" $(TARGET)
    2. 如果有自动加壳,你需要调整序号,让 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);
            }

下载

更新记录

  • v0.1.1: 修正 GenerateVArgsProxyCode 的代码生成(如 取余数 传参),感谢 谁的坏叔叔 报告。
  • v0.1: 初版发布

结语

代码写得比较乱,大概率不会有后续更新了…

Xbyak 生成字节码,调试错误的时候也是磕磕碰碰。因为都是手动插入的垃圾指令,早期写得那些代码生成的混淆“相对温和”。后期整得有点走火入魔,也尝试了下隐藏常数。

一开始就想着对抗下 “一键 PUSH”,这个目的倒是达到了。不过其它的特征… 手动根本就处理不完。

.text 外的内容如果有匹配上特征码也会尝试进行魔改。读者有兴趣可以限制特征码检索为 .text 区段的代码。

不过,这些混淆/膨胀的手段在 IDA 的帮助下分析起来倒是不怎么费劲就是…

碎碎念

  • 易语言的编译顺序太“稳定”了,可以通过定位目标函数附近的函数来快速定位。
  • 没处理过特征的函数依然可以手动检索特征码找到。
  • 再做下去感觉需要自动化识别函数、反编译、然后随机混淆/膨胀了。
    • 要考虑的东西太多了,不适合我。

致谢

免费评分

参与人数 11威望 +2 吾爱币 +112 热心值 +10 收起 理由
笙若 + 1 + 1 谢谢@Thanks!
Fiftyisnt100 + 1 + 1 谢谢@Thanks!
小朋友呢 + 2 + 1 热心回复!
hxh8672 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
beatone + 1 热心回复!
lvbuqing + 2 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
iTMZhang + 1 + 1 用心讨论,共获提升!
mmffddyy + 1 + 1 我很赞同!
_缘于天际的星i + 1 用心讨论,共获提升!
fengbolee + 2 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
苏紫方璇 + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| 爱飞的猫 发表于 2024-3-28 18:06
52xingchenqq 发表于 2024-3-28 16:06
下载双击无反应 win10系统

请参考使用方法段落,将编译后的可执行文件拖放到主程序上自动处理。

如果需要查看日志或指定额外参数,请使用命令行或终端执行。
zxxiaopi 发表于 2024-8-25 07:49
好东西是好东西!也能处理!但是写的正常的程序,上传到VT查毒,75家,报毒36家,哎,delphi写个空的报毒才几个。MD!这么歧视易语言吗?

点评

被利用来干坏事的太多了是这样的。  详情 回复 发表于 2024-8-25 17:41
tingyucloud 发表于 2024-3-23 08:26
souny 发表于 2024-3-23 08:55
这是处理特征的对吧
souny 发表于 2024-3-23 09:04
大佬 提示这个 是正常的吗 找不到文件 ERR: file does not exist.

点评

你是怎么执行它的呢 如果是集成到易语言的话,我之前更新了下主贴,把 target 变量左右的引号删除了。这个多余的引号会冲突。  详情 回复 发表于 2024-3-23 09:17
 楼主| 爱飞的猫 发表于 2024-3-23 09:17
souny 发表于 2024-3-23 09:04
大佬 提示这个 是正常的吗 找不到文件 ERR: file does not exist.

你是怎么执行它的呢

如果是集成到易语言的话,我之前更新了下主贴,把 target 变量左右的引号删除了。这个多余的引号会冲突。
souny 发表于 2024-3-23 09:22
爱飞的猫 发表于 2024-3-23 09:17
你是怎么执行它的呢

如果是集成到易语言的话,我之前更新了下主贴,把 target 变量左右的引号删除了。 ...

202432392134288.png 大佬你看看这样是对的吧

点评

后面的分号要去掉哦…  详情 回复 发表于 2024-3-23 09:26
souny 发表于 2024-3-23 09:22
souny 发表于 2024-3-23 09:22
大佬你看看这样是对的吧

让咱们这个先执行 前面没有执行的了
 楼主| 爱飞的猫 发表于 2024-3-23 09:26
souny 发表于 2024-3-23 09:22
大佬你看看这样是对的吧

后面的分号要去掉哦…
souny 发表于 2024-3-23 09:27
爱飞的猫 发表于 2024-3-23 09:26
后面的分号要去掉哦…

果然可以了 感谢大佬!
谁的坏叔叔 发表于 2024-3-23 11:24
不行哦
aa.jpg

点评

我晚点看看,是计算除法/取模的时候报错吗?  详情 回复 发表于 2024-3-23 18:00

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
爱飞的猫 + 1 + 1 在 v0.1.1 已修正该问题,感谢报告!

查看全部评分

您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-4 01:24

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表