免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!
码云地址(master分支):https://gitee.com/dye_your_fingers/sro_-ex.git
码云版本号:80d8703b18c38ce4aefb2ff9a5c8254747b59833
代码下载地址,在 SRO_EX 目录下,文件名为:SRO_Ex-逆向分析隐藏壳代码解析不正常的BUG.zip
链接:https://pan.baidu.com/s/1W-JpUcGOWbSJmMdmtMzYZg
提取码:q9n5
--来自百度网盘超级会员V4的分享
HOOK引擎,文件名为:黑兔sdk.zip
链接:https://pan.baidu.com/s/1IB-Zs6hi3yU8LC2f-8hIEw
提取码:78h8
--来自百度网盘超级会员V4的分享
以 https://blog.csdn.net/qq_36301061/article/details/135093433 它的代码为基础进行修改
由于游戏代码原本的代码我们给放到了游戏exe文件的最后,以防止我们的dll文件被剥离做的壳,然后为了避免被人一眼看出游戏原本的代码被我们放到了最后,所以对这段代码进行加密(^运算符),如果把代码加密了别人就看不出游戏exe文件最后的数据是代码了,如果我们把游戏原本的代码放到其它数据库或者其它位置,当使用是通过网络下载的方式就不需要对代码加密了。壳加密解密其实没必要,壳重要的是在于使用方式。
PEProtecter.cpp文件的修改,修改了 Save函数
#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); // 写入客户端数据
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;
}
GameProtect.cpp文件的修改,修改了 InitEntryCode函数
#include "pch.h"
#include "GameProtect.h"
GameProtect* _protect;
extern int client;
unsigned _stdcall GetFunctionAddress(int index) {
//CString txt;
//txt.Format(L"接收到:%d", index);
//AfxMessageBox(txt);
return _protect->GetAddress(index);
}
unsigned GameProtect::GetAddress(int index)
{
//CString txt;
unsigned result = (unsigned)this->_EntryCode[index];
//txt.Format(L"index:%d获取地址:%x", index, result);
// AfxMessageBox(txt);
return result;
}
unsigned GameProtect::GetAddressHide(unsigned _eip)
{
//CString txt;
for (int i = 0; i < _HideCount; i++){
if (_HideCode[i].Start == _eip) {
return (unsigned)_EntryCode[_HideCode[i].Index];
}
}
//txt.Format(L"index:%d获取地址:%x", index, result);
// AfxMessageBox(txt);
return 0;
}
GameProtect::GameProtect()
{
//AfxMessageBox(L"122222");
_protect = this;
if (!InitEntryCode()) {
AfxMessageBox(L"程序加载失败!");
ExitProcess(0);
}
CString txt;
txt.Format(L"111");
AfxMessageBox(txt);
}
bool GameProtect::MulCheckBySempore()
{
auto hMuls = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, L"system_seamp");
if (!hMuls) {
hMuls = CreateSemaphore(0, 3, 3, L"system_seamp");
}
if (WaitForSingleObject(hMuls, 0) == WAIT_TIMEOUT) return true;
return false;
}
void GameProtect::CheckMult()
{
if (MulCheckBySempore()) {
AfxMessageBox(L"当前客户端启动已经超过最大数量");
ExitProcess(0);
}
}
LONG _stdcall PVEHandl(PEXCEPTION_POINTERS val) {
if (val->ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) {
unsigned _eip = val->ContextRecord->Eip;
unsigned _eipReal = _protect->GetAddressHide(_eip);
/* CString txt;
txt.Format(L"PVEHandl当前地址:%X", _eipReal);
AfxMessageBox(txt);*/
if (_eipReal) {
val->ContextRecord->Eip = _eipReal;
return EXCEPTION_CONTINUE_EXECUTION; // 继续执行
}
else return EXCEPTION_CONTINUE_SEARCH;
}
return EXCEPTION_CONTINUE_SEARCH;
}
bool GameProtect::InitEntryCode()
{
TCHAR FileModule[0x100];
GetModuleFileName(NULL, FileModule, 0x100);
auto hFile = CreateFile(FileModule, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
return false;
}
DWORD dRead;
DWORD filelen = GetFileSize(hFile, &dRead);
char* _data = new char[filelen];
if (ReadFile(hFile, _data, filelen, &dRead, NULL)) {
char* _dataBegin = _data;
unsigned* _uRead = (unsigned*)(_data + filelen - 4);
for (int i = 0; i < filelen - _uRead[0]-4; i++) // 解密数据
{
_data[_uRead[0] + i] = _data[_uRead[0] + i] ^ 0x23;
}
//unsigned* _uRead = (unsigned*)_data[filelen - 4];
filelen = _uRead[0];// 真实的文件大小
_uRead = (unsigned*)(_data + filelen);
unsigned code_count = _uRead[0];
_data = _data + filelen + sizeof(code_count);
PCODEContext _ContextArrys = (PCODEContext)_data;
for (int i = 0; i < code_count; i++)
{
if (_ContextArrys[i].hide) {
_HideCount++;
}
}
if (_HideCount > 0) {
AddVectoredExceptionHandler(1, PVEHandl);
_HideCode = new HIDE_CODE[_HideCount];
_HideCount = 0;
}
_data = _data + sizeof(CODEContext) * code_count;
_EntryCode = new LPVOID[code_count];
for (int i = 0; i < code_count; i++)
{
char* _tmpByte = new char[_ContextArrys[i].len + 2];
_EntryCode[i] = _tmpByte;
_tmpByte[0] = 0x9D;
_tmpByte[1] = 0x61;
/*CString txt;
txt.Format(L"当前地址:%X", _EntryCode[i]);
AfxMessageBox(txt);*/
unsigned offset = sizeof(_ContextArrys[i].r_count) * _ContextArrys[i].r_count;
memcpy((char*)_EntryCode[i] + 2, _data + offset, _ContextArrys[i].len);
unsigned short* rel = (unsigned short*)_data;
for (int x = 0; x < _ContextArrys[i].r_count; x++)
{
unsigned* _callAddr = (unsigned*)((char*)_EntryCode[i] + rel[x] + 1 + 2);
_callAddr[0] = _callAddr[0] - (unsigned)_callAddr - 4;
// AfxMessageBox(L"这里代码存在问题,后面改");
}
_data = _data + offset + _ContextArrys[i].len;
DWORD dOld;
VirtualProtect(_EntryCode[i], _ContextArrys[i].len, PAGE_EXECUTE_READWRITE, &dOld);
if (_ContextArrys[i].hide) {
_EntryCode[i] = (LPVOID)((unsigned)_EntryCode[i] + 2);
_HideCode[_HideCount].Index = i;
_HideCode[_HideCount].Start = _ContextArrys[i].start;
_HideCount++;
}
}
delete[]_dataBegin;
}
else return false;
auto hMod = GetModuleHandle(NULL);
unsigned addMod = (unsigned)hMod;
unsigned addReset = addMod + 0xC2EFFC;
DWORD dOld = GetFunctionAddress(0);
// ::VirtualProtect((LPVOID)addReset, 4, PAGE_EXECUTE_READWRITE, &dOld);
// ::VirtualProtect(this->_GameCode, 0x1000, PAGE_EXECUTE_READWRITE, &dOld);
unsigned* read = (unsigned*)addReset;
read[0] = (unsigned)this->_EntryCodeEx;
//_EntryCode[1] = GetFunctionAddress;
read = (unsigned*)(this->_EntryCodeEx + 1);
read[0] = (unsigned)GetFunctionAddress - 5 - (unsigned)(this->_EntryCodeEx);
return true;
}