JoyHD

2014年4月3日 星期四

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

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

嵌入ASM數據需要使用內聯彙編(inline assembly),在Visual C++最基本的樣子如下:
void __declspec(naked) __stdcall 數據名稱()
{
__asm
{
// 數據
}
}

狀況一

我們拿 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:
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));
}
}

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

狀況二

以 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));
}

接著開關處可以直接這樣改:
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)。

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

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

未完待續。


1 2

16 則留言:

  1. 作者已經移除這則留言。

    回覆刪除
  2. 寫的很棒,期待後續的教學。

    回覆刪除
  3. 小弟幾乎是外行人
    但照著本篇操作還是成功了
    只會添加一些動態數據和簡單的ICS
    較複雜的ICS就完全搞不懂了

    像遇到這個:
    [Disable]
    0159E56C:
    DD IsRectEmpty

    IsRectEmpty這個要怎麼轉呢

    ---------------------------
    // TwMS v170.3_ICS_物理無敵
    // 更新:tsuan2000

    [Enable]
    Alloc(PhysicalGod, 32)

    PhysicalGod:
    Cmp [Esp],007B325C
    Jne IsRectEmpty
    Xor Eax,Eax
    Inc Eax
    Ret 0004

    0159E56C:
    DD PhysicalGod

    [Disable]
    0159E56C:
    DD IsRectEmpty
    DeAlloc(PhysicalGod)

    回覆刪除
    回覆
    1. 這種用到API的數據還沒教,請等以後的教學。

      刪除
    2. 亂搞一翻功能用出來了:o

      ----------------------------------------
      DWORD PhysicalGodyAddress = 0x0159E56C;
      DWORD PhysicalGody_Disable = (DWORD)IsRectEmpty;

      void __declspec(naked) __stdcall PhysicalGody()
      {
      __asm
      {
      Cmp dword ptr[Esp],0x007B325C
      Jne PhysicalGody_Back
      Xor Eax,Eax
      Inc Eax
      Ret 00004
      PhysicalGody_Back:
      Jmp PhysicalGody_Disable
      }
      }
      DWORD PhysicalGody_Enable = (DWORD)PhysicalGody;
      --------------------------------------------------------
      小小請問一下
      DWORD PhysicalGody_Disable = (DWORD)IsRectEmpty;
      為什麼(DWORD)IsRectEmpty改別的編譯就過不了
      太神奇啦

      刪除
    3. 不錯喔差不多是這樣,不過並不是每個數據都有辦法這樣子做,通用的方法要用GetProcAddress,你有空可以自己查看看,學會了就是你的了。

      刪除
    4. Wow Toby大太神了!
      感恩感恩,期待以後的教學,我會經常來光顧的。

      刪除
  4. 托比大大,請問:現在有另外的防護嗎?
    我試了好多種數據,狀況一和狀況二的,好幾種
    無論是PLAY就注入打勾、或是PLAY注入,遊戲中打勾,
    外掛界面和遊戲都會直接消失,
    不知道是不是我少做了什麼

    回覆刪除
    回覆
    1. 躺著玩、坐著玩、趴著玩還是八仙好玩
      野野野野野
      首先你要確認你的數據是正確的,而不是隨便找到了就拿來用,確認完畢再確認程式是正確的,再來還是一樣的話可能這個數據不能用了也說不定。

      刪除
    2. 確認過用AutoASM是可用的數據,只是想確認不需要做別的事,只要直接寫入就能用

      刪除
    3. //TwMs_V172.1_ICS_技能動畫消除
      //更新時間:2014-07-24
      [enable]
      alloc(NoSkillAnimation,128)

      NoSkillAnimation:
      Cmp [Esp], 00D0D16A
      Jne 00D3A566
      Mov [Esp], 00D15081
      Jmp 00D3A566

      013A6F28:
      DD NoSkillAnimation

      [disable]
      013A6F28:
      DD 00D3A566
      dealloc(NoSkillAnimation)

      刪除
    4. DWORD NoSkillAnimation_Address = 0x013A6F28;
      DWORD NoSkillAnimation_Disable = 0x00D3A566;

      void __declspec(naked) __stdcall NoSkillAnimation()
      {
      __asm
      {
      Cmp dword ptr[Esp], 0x00D0D16A
      Jne NoSkillAnimationBack
      Mov dword ptr[Esp], 0x00D15081
      NoSkillAnimationBack:
      Jmp NoSkillAnimation_Disable
      }
      }
      DWORD NoSkillAnimation_Enable = (DWORD)NoSkillAnimation;

      void FormMain::checkBox1_CheckedChanged(System::Object^ sender, System::EventArgs^ e)
      {
      if (this->checkBox1->Checked)
      {
      memcpy((void *)NoSkillAnimation_Address, &NoSkillAnimation_Enable, sizeof(NoSkillAnimation_Enable));
      }
      else
      {
      memcpy((void *)NoSkillAnimation_Address, &NoSkillAnimation_Disable, sizeof(NoSkillAnimation_Disable));
      }
      }

      刪除
    5. 真的檢查過很多遍,不知道是哪邊出了問題,
      也許會讓托比大覺得煩,但我真的想學會啊..@_@

      刪除
    6. 上面我貼錯了,還有一行Jmp NoSkillAnimationBack

      刪除
  5. 不才小弟剛踏入這塊領域
    剛剛試著用網路上找來的人物HP、MP pointer
    想要顯示HP和MP
    但發現顯示出來的結果都只有正確值的一半
    例如我HP是3124,但找出來卻是1562
    為啥會這樣

    我的code:
    #define R(adr) (*(long*)(adr))
    #define P(adr, off) ( R( (R(adr) + off) ) )
    long hp = P(0x01665BB0, 0x000027F4);
    String ^Str;
    Str=hp.ToString();
    label1->Text=Str;

    回覆刪除

注意:只有此網誌的成員可以留言。