码云地址(master分支):https://gitee.com/dye_your_fingers/sro_-ex.git
码云版本号:c90bfbd6bd91f504b5e981558a9b2dd73a93ac88
代码下载地址,在 SRO_EX 目录下,文件名为:SRO_Ex-修改随机基址为固定基址.zip
链接:https://pan.baidu.com/s/1W-JpUcGOWbSJmMdmtMzYZg
提取码:q9n5
--来自百度网盘超级会员V4的分享
HOOK引擎,文件名为:黑兔sdk.zip
链接:https://pan.baidu.com/s/1IB-Zs6hi3yU8LC2f-8hIEw
提取码:78h8
--来自百度网盘超级会员V4的分享
以 网游逆向分析与插件开发-代码保护壳的优化-对壳数据进行加密与解密-CSDN博客 它的代码为基础进行修改
遇到客户端随机基址的问题改怎样处理?这个随机基址的东西是可以给它调成固定基址的,Visual Studio在编译的时候是有一个选项的,可以把随机基址取消掉它就变成固定基址了,对于一个可执行的exe文件来说随机基址变成固定基址是对它没有影响的,exe文件相当于它是第一个启动的,内存空间它随便用,只要不调其它的,默认400000的地址没人会跟它抢,所以这就没有任何问题,具体怎样调?在exe文件里的文件头里有一个字段,如下图,image_file_hjeader.Characteristics它是一个short类型有16bit,每一个bit分别对应下图中的 #define,也就是计算机中只能写0或1,如果16bit中第二个位置的值是1表示#define image_file_executable_image是可执行文件,如果值是0表示不是可执行文件这样理解这
强制为固定基址,对于dll文件无效,因为dll文件它自己不能决定,因为dll编译时固定的内容可能会被其它dll抢占了,所以它是没有用的,只能对应用程序(exe文件)有用,因为exe文件是第一个加载不存在内存空间被抢问题
PEProtecter.cpp文件的修改,修改了 LoadFile函数
#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;
/**
强制为固定基址,对于dll文件无效,因为dll文件它自己不能决定,因为dll编译时固定的内容可能会被其它dll抢占了,所以它是没有用的
只能对应用程序(exe文件)有用
*/
ntHeader32->FileHeader.Characteristics = ntHeader32->FileHeader.Characteristics | IMAGE_FILE_RELOCS_STRIPPED;
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); // 写入客户端数据
unsigned _dataHeadlenth = (unsigned)_dataHead - (unsigned)_dataHeadBegin;
unsigned _dataEntrylenth = (unsigned)_dataEntry - (unsigned)_dataEntryBegin;
// 加密数据
for (int i = 0; i < _dataHeadlenth; i++)
{
_dataHeadBegin[i] = _dataHeadBegin[i] ^ 0x23;
}
for (int i = 0; i < _dataEntrylenth - 4; i++)
{
_dataEntryBegin[i] = _dataEntryBegin[i] ^ 0x23;
}
// SetFilePointer(hFile, 0, 0,FILE_END);
WriteFile(hFile, _dataHeadBegin, _dataHeadlenth, &dRead, NULL);
// SetFilePointer(hFile, 0, 0, FILE_END);
WriteFile(hFile, _dataEntryBegin, _dataEntrylenth, &dRead, NULL);
CloseHandle(hFile);
_dataHead = _dataHeadBegin;
_dataEntry = _dataEntryBegin;
return true;
}