第一次寫外掛就上手 - 使用Visual C++ 2010(二)

上一篇教了如何建立專案、建立檔案,並且實作了一個CRC數據功能。
本篇要教如何加入ICS數據功能,還沒有建立專案的請先照上一篇操作。

嵌入ASM數據需要使用內聯彙編(inline assembly),在Visual C++最基本的樣子如下:

void __declspec(naked) __stdcall 數據名稱()
{
__asm
{
// 數據
}
}

狀況一

我們拿v159.2 ICS 超級定怪來示範,數據如下:

v159.2 ICS 超級定怪
//Author: Onion
[Enable]
Alloc(HookEsp, 128)

HookEsp:
Cmp [Esp], 00B32FAC
Jne 00B32FCB
Mov [Esp], 00B32FC3
Jmp 00B32FCB

00FCA738:
DD HookEsp
[Disable]
00FCA738:
DD 00B32FCB
DeAlloc(HookEsp)

我們要將數據改寫並且在FormMain.h新增一個CheckBox,比照上一篇的作法,然後加入程式碼到FormMain.cpp

FormMain.cpp
DWORD FreezeMobsAddress = 0x00FCA738;
DWORD FreezeMobs_Disable = 0x00B32FCB;
void __declspec(naked) __stdcall FreezeMobs()
{
__asm
{
Cmp dword ptr[Esp], 0x00B32FAC
Jne FreezeMobsBack
Mov dword ptr[Esp], 0x00B32FC3
FreezeMobsBack:
Jmp FreezeMobs_Disable
}
}

DWORD FreezeMobs_Enable = (DWORD)FreezeMobs;
void FormMain::checkBox2_CheckedChanged(System::Object^  sender, System::EventArgs^  e)
{
if (this->checkBox2->Checked)
{
memcpy((void *)FreezeMobsAddress, &FreezeMobs_Enable, sizeof(FreezeMobs_Enable));
}
else
{
memcpy((void *)FreezeMobsAddress, &FreezeMobs_Disable, sizeof(FreezeMobs_Disable));
}
}

請仔細觀察程式碼與數據之間的關聯,並試著加入其他功能測試。
注意dword ptr0x

狀況二

v159.2 ICS 全職全圖打示範,數據如下:

v159.2 ICS 全職全圖打
[Enable]
Registersymbol(SuperMapOnOff)
Alloc(SuperMapOnOff, 4)
Alloc(CheckESP, 256)
Label(FullMapAttack)

SuperMapOnOff:
DD 01

CheckESP:
Cmp [SuperMapOnOff], 0
Je  0056D88A
Cmp [Esp+124], 006A3489
Jne 0056D89C
Mov [Esp+124], FullMapAttack
Jmp 0056D89C

FullMapAttack:
lea edi, [esi+00000728]
push edi
lea ecx, [esi+00000740]
add Esp, 04
push esi
mov esi, ecx
mov eax, [011C7274]
mov eax, [eax+94C8]
push eax
lea ecx, [esi+0c]
call 0042C3F9
mov eax, [011C7274]
mov eax, [eax+94CC]
push eax
mov ecx, esi
call 0042C3F9
mov eax, esi
pop esi
Jmp 006A349B

00F282C8:
DD CheckESP

[Disable]
00F282C8:
DD 0056D89C
DeAlloc(CheckESP)

程式碼如下:

DWORD FullMapAttack_OnOff = 0;
DWORD FullMapAttack_Address = 0x00F282C8;
DWORD FullMapAttack_Disable = 0x0056D89C;
DWORD FullMapAttack_Call = 0x0042C3F9;
DWORD FullMapAttack_Jmp = 0x006A349B;
void __declspec(naked) FullMapAttack_Main()
{
__asm
{
lea edi,[esi+0x00000728]
push edi
lea ecx,[esi+0x00000740]
add Esp, 0x04
push esi
mov esi,ecx
mov eax,[0x011C7274]
mov eax,[eax+0x94C8]
push eax
lea ecx,[esi+0x0c]
call FullMapAttack_Call
mov eax,[0x011C7274]
mov eax,[eax+0x94CC]
push eax
mov ecx,esi
call FullMapAttack_Call
mov eax,esi
pop esi
Jmp FullMapAttack_Jmp
}
}
DWORD FullMapAttack_Main_Address = (DWORD)FullMapAttack_Main;
void __declspec(naked) FullMapAttack()
{
__asm
{
Cmp dword ptr[FullMapAttack_OnOff], 0
Je  FullMapAttackBack
Cmp dword ptr[Esp+0x124], 0x006A3489
Jne FullMapAttackBack
Push FullMapAttack_Main_Address
Pop dword ptr[Esp+0x124]
FullMapAttackBack:
Jmp FullMapAttack_Disable
}
}
DWORD FullMapAttack_Enable = (DWORD)FullMapAttack;

由於加上了開關FullMapAttack_OnOff,我們可以改用更方便的寫法來寫開關。
先把HOOK程式碼移出來放到一個獨立的函數(function):

void InjectScript()
{
memcpy((void *)FullMapAttack_Address, &FullMapAttack_Enable, sizeof(FullMapAttack_Enable));
}

接著開關處可以直接這樣改:

FormMain.cpp
void FormMain::checkBox3_CheckedChanged(System::Object^  sender, System::EventArgs^  e)
{
if (this->checkBox3->Checked)
{
// memcpy((void *)FullMapAttack_Address, &FullMapAttack_Enable, sizeof(FullMapAttack_Enable));
FullMapAttack_OnOff = 1;
}
else
{
// memcpy((void *)FullMapAttack_Address, &FullMapAttack_Disable, sizeof(FullMapAttack_Disable));
FullMapAttack_OnOff = 0;
}
}

但要記得在Main()中呼叫(call)InjectScript()才會寫入數據。
在第三行先宣告(declare)函數原型(prototype),往後的程式碼才能呼叫InjectScript()而不會產生編譯錯誤(compilation error)。

FormMain.cpp
void InjectScript();
/* 略 */
void Main(void)
{
InjectScript();
/* 略 */
}

進階:在寫入開關值時,也能直接將dynamic_cast<CheckBox ^>(sender)->Checked賦值(assign)給變數(variable),程式碼將更為簡潔。

###警告:由於遊戲有記憶體保護,部分功能可能需在PLAY畫面打勾才有效,在遊戲內打勾可能造成沒有回應且會造成遊戲不正常結束,部分電腦在遊戲不正常結束後會有當機情形;關閉遊戲按鈕亦同,在部分電腦會有當機問題。以上問題均出自於遊戲本身,由於本程式只供學習之用,當機造成的風險請自行承擔,往後的教學會提到如何避免發生上述問題。

未完待續。

#第 1 2 3