[Hook技术]利用inlineHook来实现快速破解易语言程序

前几天笔者在逆向一个易语言写的身份证生成器程序的时候,发现易语言的各种函数都是经过封装过的,包括文本比较,字节集比较等敏感的功能函数全都是经过封装的,并且功能特征码无论哪个版本的易语言还完全相同…
现在网上的一些易语言特征码根本不可靠,只要把代码稍微VM一下,特征码就找不到了,在此,笔者稍微研究了一下易语言库函数调用过程:
测试程序的源码如下:
易语言测试程序代码
先看看”信息框”函数是怎么被调用的

信息框的汇编代码

可见易语言调用库函数是保留了参数的属性,所以实际传递的参数比代码写的参数要多

调用跳转

跳转到调用库函数的函数的主体

调用库函数

这边是调用库函数,之前设置的EBX就是库函数主体的位置了

信息框库函数主体

终于到库函数的主体部分了…(调用一个函数经历这么多代码,怪不得易语言代码效率这么低)

文本比较调用过程

顺便贴上文本比较的代码部分,可见易语言文本比较也是通过调用库函数实现的

那么只要找到想要Hook的易语言库函数的特征码,就能轻而易举的实现Hook
这里笔者采用DLL注入的方式来实现inlinehook,特征码记录在自定义的配置文件里面
配置定义:

enum EasyLanTypes { zjx, dzsx, zsx, xsx, sjdxsx, wbx, zjj, ljx, noreturn };
typedef struct {
    BYTE *pBin;
    UINT lenth;
} BIN;
typedef struct {
    string MsgText;
    UINT OffsetToESP;
    EasyLanTypes ArgType;
} ArgInfo;
typedef struct {
    BIN SignatureCode;
    DWORD ESPOffsetToReturnAddress;
    EasyLanTypes ReturnValueType;
    string FunName;
    vector<ArgInfo> ArgSettings;
    DWORD HookId;
    DWORD CutByteSize;
    BIN RunCode;
} ConfigInfo;

处理:

BOOL Attch(const char *ConfigFile) {

    CreateConsole();//创建一个控制台以便调试

    HANDLE ProcessHandle = INVALID_HANDLE_VALUE;
    LoadConfig(ConfigFile);//载入配置文件
    if (!config.size())return false;
    int i, j;
    DWORD AddressOfSignatureCode;
    DWORD BaseAddress = (DWORD)GetModuleHandle(NULL);
    IMAGE_DOS_HEADER *pidh = (IMAGE_DOS_HEADER *)BaseAddress;
    IMAGE_NT_HEADERS *pinh = (IMAGE_NT_HEADERS *)(BaseAddress + pidh->e_lfanew);
    IMAGE_SECTION_HEADER *pish = IMAGE_FIRST_SECTION(pinh);
    DWORD old;
    DWORD AddressForHookCall = HookCall();
    DWORD OffsetOfJmp;
    //一般第一个区段就是代码段了,改变代码段的属性
    VirtualProtect((LPVOID)(BaseAddress + pish->VirtualAddress), pish->Misc.VirtualSize, PAGE_EXECUTE_READWRITE, &old);

    printf("代码块地址:%x   大小:%x\n", BaseAddress + pish->VirtualAddress, pish->Misc.VirtualSize);
    printf("Hook回调函数地址:%x\n", AddressForHookCall);
    //解析配置文件
    /*格式:[特征码][覆盖指令大小][返回地址与ESP的偏移量][返回值的类型][函数名] argconfig:   [参数值类型][参数与ESP的偏移量][参数名]

    enum EasyLanTypes { 字节型 = 0, 短整数型 = 1, 整数型 = 2, 小数型 = 3, 双精度小数型 = 4, 文本型 = 5, 字节集 = 6, 逻辑型 = 7, 没有返回值 = 8 };*/
    for (i = 0; i < config.size(); i++) {
        DWORD OffsetOfAddressOfSignatureCode = SeachMemory((const BYTE *)(BaseAddress + pish->VirtualAddress), pish->Misc.VirtualSize, config[i].SignatureCode.pBin, config[i].SignatureCode.lenth);
        if (!OffsetOfAddressOfSignatureCode)continue;
        AddressOfSignatureCode = BaseAddress + pish->VirtualAddress + OffsetOfAddressOfSignatureCode;

        printf("%s 特征码:%s\n", config[i].FunName.data(), BinToText(config[i].SignatureCode).data());

        if (AddressOfSignatureCode == 0)continue;
        if (config[i].CutByteSize < 5)continue;

        printf("Hook地址:%x\n", AddressOfSignatureCode);

        UINT emptybytesize = config[i].CutByteSize - 5;
        //HookId设置为特征码的位置,Hook处理程序通过函数返回地址来得到HookId,所以特征码一定要找在函数头部的代码
        config[i].HookId = AddressOfSignatureCode;
        //构造返回函数的代码,执行完Hook的代码之后执行原函数头部的指令,再正常执行函数原有的代码
        config[i].RunCode.lenth = config[i].CutByteSize;
        config[i].RunCode.pBin = new BYTE[config[i].RunCode.lenth + 6];
        memcpy(config[i].RunCode.pBin, (LPVOID)AddressOfSignatureCode, config[i].RunCode.lenth);
        BYTE Attchbyte[6] = { 104,0,0,1,0,195 };
        DWORD JmpAddress = AddressOfSignatureCode + 5;
        memcpy(&Attchbyte[1], &JmpAddress, 4);
        memcpy(&config[i].RunCode.pBin[config[i].RunCode.lenth], Attchbyte, 6);
        //构造跳转指令(CALL)
        OffsetOfJmp = AddressForHookCall - (AddressOfSignatureCode + 5);
        BYTE JmpCode[5] = { 232,0,0,0,0 };
        memcpy(&JmpCode[1], &OffsetOfJmp, 4);
        //写入函数头部
        memcpy((LPVOID)AddressOfSignatureCode, JmpCode, 5);
        //用NOP填充多余指令字节
        while (emptybytesize) {
            memset((LPVOID)(AddressOfSignatureCode + 5 + (emptybytesize-- - 1)), 144, 1);
        }

    }
    return true;
}

配置格式:[特征码][覆盖指令大小][返回地址与ESP的偏移量][返回值的类型][函数名] argconfig: [参数值类型][参数与ESP的偏移量][参数名];
参数类型: 字节型 = 0, 短整数型 = 1, 整数型 = 2, 小数型 = 3, 双精度小数型 = 4, 文本型 = 5, 字节集 = 6, 逻辑型 = 7, 没有返回值 = 8 ;
Hook处理程序通过配置文件中参数与现ESP的偏移量来获取参数内容,如果想抓取返回值,则需要修改返回地址处的代码来实现获取函数返回值

本文完整代码附配置文件下载地址

效果:
效果1
效果2

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值