45.网游逆向分析与插件开发-代码保护壳的优化-逆向分析隐藏壳代码解析不正常的BUG

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!

网游逆向分析与插件开发系列之前的内容:

https://note.youdao.com/s/7vFmT8rC

单击游戏逆向:

https://note.youdao.com/s/7vFmT8rC

码云地址(master分支):SRO_Ex: 网游逆向分析与插件开发icon-default.png?t=N7T8https://gitee.com/dye_your_fingers/sro_-ex.git

码云版本号:86e72e3570b896d23bc5701aa0b3d7474c8b1a63

代码下载地址,在 SRO_EX 目录下,文件名为:SRO_Ex-逆向分析隐藏壳代码解析不正常的BUG.zip

链接:百度网盘 请输入提取码百度网盘为您提供文件的网络备份、同步和分享服务。空间大、速度快、安全稳固,支持教育网加速,支持手机端。注册使用百度网盘即可享受免费存储空间icon-default.png?t=N7T8https://pan.baidu.com/s/1W-JpUcGOWbSJmMdmtMzYZg

提取码:q9n5

--来自百度网盘超级会员V4的分享

HOOK引擎,文件名为:黑兔sdk.zip

链接:百度网盘 请输入提取码百度网盘为您提供文件的网络备份、同步和分享服务。空间大、速度快、安全稳固,支持教育网加速,支持手机端。注册使用百度网盘即可享受免费存储空间icon-default.png?t=N7T8https://pan.baidu.com/s/1IB-Zs6hi3yU8LC2f-8hIEw

提取码:78h8

--来自百度网盘超级会员V4的分享

 以 网游逆向分析与插件开发-代码保护壳的优化-游戏扩展插件实现对隐藏壳代码的支持 它的代码为基础进行修改

PEProtecter.cpp文件的修改,之前游戏会卡主的原因是,我们一共修改了两个函数,但是代码的序号没有自增,导致第二个不隐藏的代码调用了第一个函数,修改了 PushCode函数

#include "pch.h"
#include "PEProtecter.h"

bool PEProtecter::LoadFile(wchar_t* _file)
{
    auto hFile = CreateFile(_file, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    
    if (hFile == INVALID_HANDLE_VALUE) {
        return false;
    }

    DWORD dRead;
   filelen = GetFileSize(hFile, &dRead);
    
    if (filelen < 1)return false;
    if (filelen > _datal) {
        if (_data)delete[]_data;
        _data = new char[filelen];
        _datal = filelen;
    }
    if (ReadFile(hFile, _data, filelen, &dRead, NULL)) {

        PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)_data;
        PIMAGE_NT_HEADERS32 ntHeader32 = (PIMAGE_NT_HEADERS32)(_data + dosHeader->e_lfanew);
        SecCount = ntHeader32->FileHeader.NumberOfSections;
        SecArrys = (PIMAGE_SECTION_HEADER)(sizeof(IMAGE_NT_HEADERS32) + (unsigned)ntHeader32);
        Base = ntHeader32->OptionalHeader.ImageBase;
        CloseHandle(hFile);
        if (filelen > _dataEntryl) {
            if (_dataEntry)delete[]_dataEntry;
            _dataEntry = new char[filelen];
            _dataEntryBegin = _dataEntry;
            _dataEntryl = filelen;
        }
        return true;
      
    }  

    delete[] _data;
    _data = nullptr;
    filelen = 0;

    return false;
}

unsigned PEProtecter::VATofoa(unsigned va, unsigned _base)
{
    DWORD uBase;
    _base == 0 ? uBase = Base : uBase = _base;
    DWORD rva = va - uBase;

    for (int i = 0; i < SecCount; i++) {
        IMAGE_SECTION_HEADER tempSecArr = SecArrys[i];
        if ((rva > tempSecArr.VirtualAddress)&&(rva < tempSecArr.VirtualAddress + tempSecArr.SizeOfRawData)) {
            DWORD offset = rva - tempSecArr.VirtualAddress;
            return offset + tempSecArr.PointerToRawData;
        }
    }

    return 0;
}

char* PEProtecter::ReadDataByVa(unsigned va, unsigned _base)
{
    unsigned index = VATofoa(va, _base);

    return _data + index;
}

PEProtecter::~PEProtecter()
{
    if (_data) {
        delete[] _data;
        _data = 0;
    }

    if (_dataEntryBegin) {
        delete[]_dataEntryBegin;
        _dataEntryBegin = 0;
    }

    if (_dataHeadBegin) {
        delete[]_dataHeadBegin;
        _dataHeadBegin = 0;
    }

}

CString PEProtecter::DAsm(unsigned va, unsigned len, unsigned _base)
{
    CStringA _output;
    CStringA _tmp;
    char* buffer = ReadDataByVa(va, _base);
    DISASM disasm;
    memset(&disasm, 0, sizeof(DISASM));
    disasm.EIP = (UIntPtr)buffer;
    disasm.VirtualAddr = (UIntPtr)va; // 根据这个值把 jmp、call这种跳转指令的地址重新计算,如果它的值是0表示不重新计算
    disasm.Archi = IA32;// 指令集类型,1表示32位、0表示64位,AMD64
    disasm.Options = MasmSyntax;// 汇编语法、指令形式,如masm、nasm
    
    int _len;

    while (!disasm.Error)
    {
        disasm.SecurityBlock = (UIntPtr)buffer + len - disasm.EIP;// 设置停止位置
        if (disasm.SecurityBlock <= 0) {
            break;
        }
        _len = Disasm(&disasm); // 反汇编方法,返回值是当前指令的长度
        switch (disasm.Error)
        {
            case OUT_OF_BLOCK:break;// 指令不完整,比如还有两个字节可以解读,但是这两个字节不是一个完整的指令
            case UNKNOWN_OPCODE: { // 有些指令解读不出来
                _tmp.Format("0x%.08X => ????????\r\n", (unsigned)disasm.VirtualAddr);
                _output += _tmp;

                disasm.EIP += 1;
                disasm.VirtualAddr += 1;

                break;
            }
            default:
                _tmp.Format("0x%.08X => [%s]\r\n", (unsigned)disasm.VirtualAddr, &disasm.CompleteInstr);
                _output += _tmp;
                disasm.EIP += _len;
                disasm.VirtualAddr += _len;
        }
    }
    CString result;
    result = _output;
    return result;
}

void PEProtecter::Init(unsigned count)
{
    CodeCount = count;
    unsigned _size = 4 + count * sizeof(CODEContext);
    if (_size > _dataHeadl) {
        if (_dataHead)delete[]_dataHead;
        _dataHead = new char[_size];
        _dataHeadBegin = _dataHead;
        _dataHeadl = _size;
    }
    unsigned* _count = (unsigned*)_dataHead;
    _count[0] = count;
    _dataHead += sizeof(_count);

}

void PEProtecter::PushCode(unsigned va, unsigned len, unsigned _base, bool hide)
{
    char* buffer = ReadDataByVa(va, _base); // 硬盘中的代码位置(FOA)
    DISASM disasm;
    memset(&disasm, 0, sizeof(DISASM));
    disasm.EIP = (UIntPtr)buffer;
    disasm.VirtualAddr = (UIntPtr)va; // 根据这个值把 jmp、call这种跳转指令的地址重新计算,如果它的值是0表示不重新计算
    disasm.Archi = IA32;// 指令集类型,1表示32位、0表示64位,AMD64
    disasm.Options = MasmSyntax;// 汇编语法、指令形式,如masm、nasm


    int _len;
    Int32 opcode;

    PCODEContext _pcontext = (PCODEContext)_dataHead;
    _pcontext->start = va;
    _pcontext->r_couent = 0;
    _pcontext->len = len;
    _pcontext->hide = hide;
    while (!disasm.Error)
    {
        disasm.SecurityBlock = (UIntPtr)buffer + len - disasm.EIP;// 设置停止位置
        if (disasm.SecurityBlock <= 0) {
            break;
        }
        _len = Disasm(&disasm); // 反汇编方法,返回值是当前指令的长度
        switch (disasm.Error)
        {
        case OUT_OF_BLOCK:break;// 指令不完整,比如还有两个字节可以解读,但是这两个字节不是一个完整的指令
        case UNKNOWN_OPCODE: { // 有些指令解读不出来
            /**
                只要执行到这里就说明有不可解读的指令
                可以进行 停止 或 报错
            */
            disasm.EIP += 1;
            disasm.VirtualAddr += 1;
            break;
        }
        default:
            opcode = disasm.Instruction.Opcode;
            if (opcode == 0xE8 || (opcode == 0xE9)) {
                _pcontext->r_couent++;
                unsigned short offset = (unsigned)disasm.EIP - (unsigned)buffer;
                unsigned* e8 = (unsigned*)(disasm.EIP + 1);
                unsigned callAddr = va + (unsigned)disasm.EIP - (unsigned)buffer + e8[0]+5;
                e8[0] = callAddr;
                unsigned short* _offset = (unsigned short*)_dataEntry;
                _offset[0] = offset;
                _dataEntry = _dataEntry + sizeof(offset);
            }
          
            disasm.EIP += _len;
            disasm.VirtualAddr += _len;
        }
    }

    memcpy( _dataEntry, buffer, len); // 复制客户端原本代码到我们的加密空间,这个空间可以上服务器
    _dataEntry += len; // 加密空间递增指向下一块空间
    memset(buffer, 0xCC, len); // 删除客户端代码
    if (!hide) {
        memcpy(buffer, _AsmCode, 11); // 给客户端写入跳转原本客户段代码,原本客户端的代码被我们获取了目的是让我们的插件与客户端形成耦合,从而防止我们的插件被移除
    }
    char* _pushIndex = _AsmCode + 3; // 修改序号
    _pushIndex[0]++; // 修改序号
    _dataHead += sizeof(CODEContext);
}

bool PEProtecter::Save(wchar_t* _file)
{
    unsigned* fcount = (unsigned*)_dataEntry;
    fcount[0] = filelen;
    _dataEntry += sizeof(fcount);
    auto hFile = CreateFile(_file, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE) {
        return false;
    }
    DWORD dRead;
    WriteFile(hFile, _data, filelen, &dRead, NULL); // 写入客户端数据
    // SetFilePointer(hFile, 0, 0,FILE_END);
    WriteFile(hFile, _dataHeadBegin, (unsigned)_dataHead - (unsigned)_dataHeadBegin, &dRead, NULL);
    // SetFilePointer(hFile, 0, 0, FILE_END);
    WriteFile(hFile, _dataEntryBegin, (unsigned)_dataEntry - (unsigned)_dataEntryBegin, &dRead, NULL);
    CloseHandle(hFile);

    _dataHead = _dataHeadBegin;
    _dataEntry = _dataEntryBegin;
    return true;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值