本帖最後由 帥氣冰 於 2010-5-8 21:19 編輯

來源:http://msdn.microsoft.com/en-us/library/ms680553(VS.85).aspx

我幫大翻譯成中文了,我不直接完全翻譯
有詳細翻譯和檢查過!最近在研究C++的ReadProcessMemory..發這個希望可以和大家一起討論..

語句:

BOOL WINAPI ReadProcessMemory(  __in   HANDLE hProcess,  __in   LPCVOID lpBaseAddress,  __out  LPVOID lpBuffer,  __in   SIZE_T nSize,  __out  SIZE_T *lpNumberOfBytesRead);



hProcess
[in]

UM程式研究日誌 發表在 痞客邦 留言(0) 人氣()


Microsoft公司Visual C++、TurboC++
Borland公司的Borland C++
Dev公司的DevC++
其他公司的Connective C++、Master C++
 

UM程式研究日誌 發表在 痞客邦 留言(0) 人氣()

一定要看完,我自己有看完!對自己很有幫助!
1.把C++當成一門新語言學習(和C沒啥關係!真的。);
2.《Thinking In C++》,不要看《C++變成死相》
3.看《The C++ Programming Language》和《Inside The C++ Object Model》,不要為他們很難而我們自己初學者所以就不看; 
4.不要VC、BCB、BC、MC、TC等詞彙所迷惑--他們都是集成開發環境,而我們要學的是一門語言; z&G9d E
5.不要放過何一個看上去很簡單的小編程問題--他們往往並不那麼簡單,或者可以引伸出很知識點; 
6.會Visual C++,並不說明你會C++; 
7.學class並不難,template、STL、generic programming也不過如此--難的是長期堅持實踐和不遺餘力的博覽群書;www.sunskyforum.comj2[2P0R T

8.如不是天才的話,想學編程就不要想玩遊戲--你以為你做到了,其實你的C++平並沒有和你通關的能力一起變高--其實可以時刻記住:學C++是為了編遊戲的; 

9.看Visual C++的書,是學不了C++語言的; 
10.浮躁的人容易說:XX語言不行了,應該學YY;--是你自己不行了吧!? 

11.浮躁的人容易問:我到底該學什麼;--別問,學就對了; 

12.浮躁的人容易問:XX有錢途嗎;--建議你去搶銀行;
13.浮躁的人容易說:我要中文版!我英文不行!--不行?學呀! 
14.浮躁的人容易問:XX和YY哪個好;--告訴你吧,都好--隻要你學就行; 
15.浮躁的人分兩種:a)隻觀望而不學的人;b)隻學而不堅持的人; 
16.把時髦的技術掛在嘴邊,還不如把過時的技術記在心裡; 

17.C++不僅僅是支持面向對象的程序設計語言; 
18.學習編程最好的方法之一就是閱讀源代碼;19.在任何時刻都不要認為自己的書已經足夠了;
20.請閱讀《The Standard C++ Bible》(中文版:標準C++寶典),掌握C++標準;
21.看得懂的書,請仔細看;看不懂的書,請硬著頭皮看; 
22.別指望看第一遍書就能記住和掌握什麼--請看第遍、第遍; 
23.請看《Effective C++》和《More Effective C++》以及《Exceptional C++》; 
24.不要停留在集成開發環境的搖籃上,要學會控制集成開發環境,還要學會用命令行方式處理程序; 
25.和別人一起討論有意義的C++知識點,而不是爭吵XX行不行或者YY與ZZ哪個好; 
26.請看《程序設計實踐》,並嚴格的按照其要求去做; 
17.不要因為C和C++中有一些語法和關鍵字看上去相同,就認為它們的意義和作用完全一樣;
28.C++絕不是所謂的C的「擴充」--如果C++一開始就起名叫Z語言,你一定不會把C和Z語言聯繫得那麼緊密; 
29.請不要認為學過XX語言再改學C++會有什麼問題--你隻不過又在學一門全新的語言而已; 
30.讀完了《Inside The C++ Object Model》以後再來認定自己是不是已經學會了C++; 
31.學習編程的秘訣是:編程,編程,再編程; 

32.請留意下列書籍:《C++面向對像高效編程(C++ Effective Object-Oriented Software Construction)》《面向對像軟件構造(Object-Oriented Software Construction)》《設計模式(Design Patterns)》《The Art of Computer Programming》; 
33.記住:面向對像技術不隻是C++專有的; 
34.請把書上的程序例子親手輸入到電腦上實踐,即使配套光盤中有源代碼; 
35.把在書中看到的有意義的例子擴充;

36.請重視C++中的異常處理技術,並將其切實的運用到自己的程序中; 

37.經常回顧自己以前寫過的程序,並嘗試重寫,把自己學到的新知識運用進去; 
38.不要漏掉書中任何一個練習題--請全部做完並記錄下解題思路; 
39.C++語言和C++的集成開發環境要同時學習和掌握; 
40.既然決定了學C++,就請堅持學下去,因為學習程序設計語言的目的是掌握程序設計技術,而程序設計技術是跨語言的; 
41.就讓C++語言的各種平台和開發環境去激烈的競爭吧,我們要以學習C++語言本為主; 

42.當你寫C++程序寫到一半卻發現自己用的方法很拙劣時,請不要馬上停手;請盡快將餘下的部分粗略的完成以保證這個設計的完整,然後分析自己的錯誤並重新設計和編寫(參見43); 
43.別心急,設計C++的class確實不容易;自己程序中的class和自己的class設計水平是在不斷的編程實踐中完善和發展的;
44.決不要因為程序「很小」就不遵循某些你不熟練的規則--好習慣是培養出來的,而不是一次記住的; 
45.每學到一個C++難點的時候,嘗試著對別人講解這個知識點並讓他理解--你能講清楚才說明你真的理解了; 

46.記錄下在和別人交流時發現的自己忽視或不理解的知識點; 

47.請不斷的對自己寫的程序提出更高的要求,哪怕你的程序版本號會變成Version 100.XX; .?

48.保存好你寫過的所有的程序--那是你最好的積累之一; 
50.請熱愛C++!

UM程式研究日誌 發表在 痞客邦 留言(0) 人氣()

首先 先創一個CppWebBrowser1
之後看下面囉^^
上一頁按鈕


void __fastcall TForm1::Button1Click(TObject *Sender)    //上一頁
{
CppWebBrowser1->GoBack();
}

UM程式研究日誌 發表在 痞客邦 留言(0) 人氣()

要怎讓WebBrowser 點連結時候 不跳出新網頁??   
我要讓他持續WebBrowser上開啟...
可能我不會New Windows的關西嗎?


我現在是要寫一個 點廣告才能使程式的程式
要怎才可以達成?


P.S達出來的   +分!!!!  <---濫用權力XD

 

UM程式研究日誌 發表在 痞客邦 留言(0) 人氣()

在WINDOWS,每個進程有自己獨立地址空間,這樣一個應程序就無法進入另一個進程的地址空間而不會破壞另一個進程的運行,這樣使得系統更加的穩定。但這樣一來,相的,如我們要對我們感興趣的進程進行操作也就變得複雜起來。比如,我們要為另一個進程創建的口建立子類或要想從其中一個感興趣的進程中取得一些有趣的信息(比如你想得到WIN2000用戶登錄的密碼)。而DLL注入技術就是正好可以解決這些問題。DLL注入就是將DLL插入到其它你指定的進程的地址空間中,使得我們可以對感興趣的進程進行操作。 

  在我們的DLL注入到指定的進程空間時,為了可以使我們更清楚地看到它已經成功對注入到了指定的進程空間,所以我們有需要使用一個簡單的工具來查看指定的進程空間中所載入的所有模塊,以便確定我們的DLL是否已經成功注入。如果你系統中裝有WINDOWS優化大師,那麼你可以利用它提供的進程工具來查看,沒有也沒關係,我用BCB寫了一個小工具,雖然不怎麼方便,但也可以清楚地看到指定進程空間中的所有載入模塊。 
  該工具的主要代碼如下: 

//--------------------------------------------------------------------------- 
void __fastcall TfrmMain::btLookClick(TObject *Sender) 

DWORD dwProcessId; 
BOOL bRet; 
MODULEENTRY32 hMod = {sizeof(hMod)}; 
HANDLE hthSnapshot = NULL; 
BOOL bMoreMods = FALSE; 


ListView->Clear(); 
if (Edit->Text == "") 
return; 
else 
dwProcessId = StrToInt(Edit->Text); 

// 為進程建立一個快照 
hthSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 
dwProcessId); 
if (hthSnapshot == NULL) 

MessageBox(Handle,("CreateToolhelp32Snapshot failed with error " 
+ IntToStr(GetLastError())).c_str(),"Error!", 
MB_ICONINFORMATION + MB_OK); 
return; 

// 獲取模塊列中的模塊 
bMoreMods = Module32First(hthSnapshot, &hMod); 
if (bMoreMods == FALSE) 

MessageBox(Handle,("Module32First failed with error " 
+ IntToStr(GetLastError())).c_str(),"Error!", 
MB_ICONINFORMATION + MB_OK); 
return; 

for (; bMoreMods; bMoreMods = Module32Next(hthSnapshot, &hMod)) 

TListItem *Item; 
Item = ListView->Items->Add(); 
Item->Caption = String(hMod.szExePath); 
Item->ImageIndex = 0; 

// 關閉句柄 
CloseHandle(hthSnapshot); 


  接下來就開始我們的正題吧。 
  DLL注入主要有種方法,即應用HOOK技術、創建遠程線程和特洛伊DLL三種。 

  一、應用HOOK技術進行DLL注入 
  我原來寫過有關HOOK的介紹,如果你看過了或者是以前寫過HOOK程序,那麼你已經會這種DLL注入了。它其它就是為系統或某個線程安裝一個鉤子。這裡要說的是,如果是全局鉤子,那麼你的DLL將會在進程調用時載入到意一個調用的進程的地址空間中,這樣是相當浪費資源的。此我在下載的演示中就只對某一個指定的線程安裝線程鉤子。 
  1、用BCB建立一個DLL工程(如果你用的是VC或其它,請自己對照),輸入以下代碼: 
//=========================================================================== 
// 文件: UnitLib.cpp 
// 說明: 演示利用鉤子技術進行DLL注入. 
// 將本DLL中的代碼注入到指定的進程空間. 
// 作者: 陶冶(無邪) 
//=========================================================================== 


// 函數聲明 
extern "C" __declspec(dllexport) __stdcall 
bool SetHook(DWORD dwThreadId); 
extern "C" __declspec(dllexport) __stdcall 
LRESULT CALLBACK MyProc(int nCode, WPARAM wParam, LPARAM lParam); 

static HHOOK hHook = NULL; // 鉤子句柄 
static HINSTANCE hInst; // 當前DLL句柄 


int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved) 

hInst = hinst; 
return 1; 

//--------------------------------------------------------------------------- 
// 安裝鉤子函數 
bool __declspec(dllexport) __stdcall SetHook(DWORD dwThreadId) 

if (dwThreadId != 0) 

MessageBox(NULL, ("DLL已經注入!\nThreadId = " + 
IntToStr(dwThreadId)).c_str(),"DLL", 
MB_ICONINFORMATION + MB_OK); 
    // 安裝指定線程的鉤子 
hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)MyProc, 
hInst,dwThreadId); 
if (hHook != NULL) 
return true; 
}else 

MessageBox(NULL, "DLL即將從記事本進程空間中撤出!","DLL", 
MB_ICONINFORMATION + MB_OK); 
return (UnhookWindowsHookEx(hHook)); 

return true; 


// 鉤子函數 
LRESULT CALLBACK __declspec(dllexport) __stdcall 
MyProc(int nCode, WPARAM wParam, LPARAM lParam) 

  // 因為只是演示DLL注入,所以這裡什麼也不做,交給系統處理 
return (CallNextHookEx(hHook, nCode, wParam, lParam)); 

//--------------------------------------------------------------------------- 

該DLL中有兩個函數,一個為安裝鉤子函數(SetHook),另一個為鉤子函數(MyProc)。其中安裝鉤子函數提供了一個參數,由該參數指定安裝到哪個線程,如果該參數為0,則卸載鉤子。 
  編譯該工程,即生成我們要用來注入到指定進程中的DLL文件了。 
     
2、建立測試工程。用BCB建立一個應用程序工程,在窗體中添加兩個按鈕,一個用來安裝線程鉤子,一個用來卸載。代碼如下: 
//--------------------------------------------------------------------------- 
// SetHook函數原型聲明 
typedef BOOL (WINAPI *LPSETHOOK)(unsigned long dwThreadId); 

//--------------------------------------------------------------------------- 
__fastcall TfrmMain::TfrmMain(TComponent* Owner) 
: TForm(Owner) 


//--------------------------------------------------------------------------- 
// 安裝鉤子 
void __fastcall TfrmMain::Button1Click(TObject *Sender) 

String szPath; 
LPSETHOOK lproc; 
HANDLE hDll; 
BOOL bRet; 
PROCESS_INFORMATION info; 
STARTUPINFO start; 

memset(&start, 0, sizeof(start)); 
// 取得要載入的DLL文件名 
szPath = Application->ExeName; 
szPath = szPath.SubString(0, szPath.Length() 
- String(StrRScan(szPath.c_str(),'\\')).Length()); 
szPath = szPath + "[url=file://\\DllLib.dll]\\DllLib.dll[/url]"; 
  // 載入DLL 
hDll = LoadLibrary(szPath.c_str()); 
if (hDll != NULL) 

lproc = (LPSETHOOK)GetProcAddress(hDll,"SetHook"); 
if (lproc != NULL) 

// 因為沒有適當的工具可以取得線程ID,也為了簡單起見,所以這裡新創建了一個記事本進程,以便取得它的線程ID,對其安裝鉤子,把我們的DLL注入到記事本進程中。 
bRet = CreateProcess(NULL, 
"c:\\winnt\\system32\\notepad.exe", 
NULL, 
NULL, 
TRUE, 
0, 
NULL, 
NULL, 
&start, 
&info); 
if (bRet != 0) 

if((*lproc)(info.dwThreadId) == false) 
ShowMessage("Sethook failed with error " + 
IntToStr(GetLastError())); 

else 

ShowMessage("CreateProcess failed with error " + 
IntToStr(GetLastError())); 




//--------------------------------------------------------------------------- 
// 卸載鉤子 
void __fastcall TfrmMain::Button2Click(TObject *Sender) 

String szPath; 
LPSETHOOK lproc; 
HANDLE hDll; 

szPath = Application->ExeName; 
szPath = szPath.SubString(0, szPath.Length() 
- String(StrRScan(szPath.c_str(),'\\')).Length()); 
szPath = szPath + "[url=file://\\DllLib.dll]\\DllLib.dll[/url]"; 
hDll = LoadLibrary(szPath.c_str()); 
if (hDll != NULL) 

lproc = (LPSETHOOK)GetProcAddress(hDll,"SetHook"); 
if (lproc != NULL) 
(*lproc)(0); 


//--------------------------------------------------------------------------- 
  接下來生成可執行文件,點擊第一個安裝鉤子按鈕,然後你就可以用我們最開始寫的查看模塊的工具來查看了,你將會在模塊中看到你剛才DLL的路徑及文件名,這表明我們已經成功地將自己的DLL注入到了記事本進程空間。點擊卸載按鈕後,再查看記事本進程中的模塊,將不會看到我們DLL文件的完整文件名,這表明已經成功撤消了對記事本進程的注入。 

  、利用遠程線程來進行DLL注入 
    這種方法同前一種方法相比,要顯得複雜一些,並且這種方法只能在WIN2000中使用(XP,和最新的2003不知道)。具體步驟如下: 
  1)、取得遠程進程的進程ID; 
  2)、在遠程進程空間中分配一段內存用來存放要注入的DLL完整路徑; 
  3)、將要注入的DLL的路徑寫到剛才分配的遠程進程空間; 
  4)、從Kernel32.dll中取得LoadLibray的地址; 
  5)、調用CreateRemoteThread函數以從Kernel32.dll中取得的LoadLibrary函數的地址為線程函數的地址,以我們要注入的DLL文件名為參數,創建遠程線程; 
  在第二三步中,為什麼要把我們要注入的DLL的文件名寫到遠程進程的地址空間進行操作,《WINDOWS核心編程》中是這樣描述的: 
「(要注入的DLL文件名)字符串是在調用進程的地址空間中。該字符串的地址已經賦予新創建的遠程線程,該線程將它傳遞給L o a d L i b r a r y A。但是,當L o a d L i b r a r y A取消對內存地址的引用時, D L L路徑名字符串將不再存在,遠程進程的線程就可能引發訪問違規」; 

  至於第四步中為什麼不直接對LoadLibrary進行調用,《WINDOWS核心編程》中是這樣描述的: 
「如果在對C r e a t e R e m o t e T h r e a d的調用中使用一個對L o a d L i b r a r y A的直接引用,這將在你的模塊的輸入節中轉換成L o a d L i b r a r y A的形實替換程序的地址。將形實替換程序的地址作為遠程線程的起始地址來傳遞,會導致遠程線程開始執行一些令人莫名其妙的東西。其結果很可能造成訪問違規。」 

好了,下面開始我們的例子。
 

UM程式研究日誌 發表在 痞客邦 留言(0) 人氣()

自己寫API來調好像不會HOOK會C++大大可以朝這方面努力=ˇ=
我不會C++只網路找到發一下而已


自己實現一下 ReadVirtualMemory 
==================================
NTSTATUS
NtReadVirtualMemory (
     IN HANDLE ProcessHandle,
     IN PVOID BaseAddress,
     OUT PVOID Buffer,
     IN ULONG BufferSize,
     OUT PULONG NumberOfBytesRead OPTIONAL
     )
{
    ULONG BytesCopied;
    KPROCESSOR_MODE PreviousMode;
    PEPROCESS Process;
    NTSTATUS Status;
    PAGED_CODE();
    PreviousMode = KeGetPreviousMode();
    if (PreviousMode != KernelMode) {
        Status = MiValidateUserTransfer(BaseAddress, Buffer, BufferSize);
        if (Status != STATUS_SUCCESS) {
            return Status;
        }
        if (ARGUMENT_PRESENT(NumberOfBytesRead)) {
            try {
                ProbeForWriteUlong(NumberOfBytesRead);
            } except(EXCEPTION_EXECUTE_HANDLER) {
                return GetExceptionCode();
            }
        }
    }
    BytesCopied = 0;
    Status = STATUS_SUCCESS;
    if (BufferSize != 0) {
        Status = ObReferenceObjectByHandle(ProcessHandle,
                                           PROCESS_VM_READ,
                                           PsProcessType,
                                           PreviousMode,
                                           (PVOID *)&Process,
                                           NULL);
        if (Status == STATUS_SUCCESS) {
            Status = MmCopyVirtualMemory (Process,
                                          BaseAddress,
                                          PsGetCurrentProcess(),
                                          Buffer,
                                          BufferSize,
                                          PreviousMode,
                                          &BytesCopied);
            ObDereferenceObject(Process);
        }
    }
    if (ARGUMENT_PRESENT(NumberOfBytesRead)) {
        try {
            *NumberOfBytesRead = BytesCopied;
        } except(EXCEPTION_EXECUTE_HANDLER) {
            NOTHING;
        }
    }
    return Status;
}


UM程式研究日誌 發表在 痞客邦 留言(0) 人氣()

這一節實現基本選怪,普通攻擊打怪,撿物(不含過濾)。

我們先來改變一下界面,從控件欄選擇復選框,畫在口相應位置,如圖。
  

其他的還不變,我們主要是實現基本打怪,所以其他的暫時不考慮。

下面先來描述一下算法:

如何選怪:
  
     
在武林裡,你選一個怪物後,會顯示怪物的信息,那麼在內存裡也會有一個變量來保存是否選中了怪物。我們就利這個變量判斷是否選中怪物,如沒選中,那麼調用函數,讓其選怪。

如何打怪:
  
     
遊戲裡有普通攻擊,在原來的時候還可以模擬按鍵來實現打怪,現在不可以了,那麼我們只有通過調用遊戲的內部函數來實現打怪了。判斷選中怪物後,就調用普通攻擊函數,直到怪物的血值為0 
和 選中怪物變量 為0 ,就代這個怪物打死了,就需要開始下一輪選怪打怪。

如何撿物:
     
  
撿物在原來也可以模擬按鍵,現在也是調用內部函數。為這一節不牽涉物品過濾,所以有什麼我們就撿什麼,呵呵。


程序簡單流程:
在主進程不斷的讀取遊戲信息,開闢一個新的線程,放置我們的打怪代碼。這裡已經開始使用線程了,大看到多線程也不用怕,其實沒什麼,在BCB 
,多線程就相當於一個模塊一樣,也是一個單元文件,不同的是它和主線程的代碼有點區別,線程代碼需要我們用代碼開啟它的運行。從這裡就是使用多線程,也是方便以後編寫,後面的將會很多地方用到多線程。因為現在我們只開啟了2個線程,並且不會衝突,所以暫時也不用做線程同步。

在新的線程裡,寫一個循環,並用變量控制循環開始和結束。
將打怪的代碼全部寫到循環裡。
順序大致如下:
選怪:
判斷是否已經選中怪物
如果選中怪物則跳到打怪部分,如果沒選中,則過一段時間繼續選怪。

打怪:
用普通攻擊  攻擊怪物,直到怪物死亡。

撿物:怪物死亡一段時間後,開始撿物。


這裡只講如何使用遊戲內部函數,分析部分還是需要大家自己到廣海看帖子學習,因為一天兩天也學不會。如果在寫完小秘的帖子後有時間的話,我可能寫寫分析部分的帖子。

下面是實現代碼:
打開我們上次的工程,文件-新建-其他-選擇最下面的Thread 
Object ,Thread Object 就是線程類。
並命名為 ThreadKill ,在工程窗口雙擊打開 ThreadKill 
,進入代碼編輯界面。
__fastcall ThreadKill::ThreadKill(bool CreateSuspended)
  
  : TThread(CreateSuspended)
{
}
將 
FreeOnTerminate=true;  插入那對括號中。
__fastcall 
ThreadKill::ThreadKill(bool CreateSuspended)
    : 
TThread(CreateSuspended)
{
    
FreeOnTerminate=true;
}
一般用多線程都會寫這麼一句,為了在線程結束後釋放資源。

void __fastcall 
ThreadKill::Execute() 
這裡就是線程執行的地方,需要執行的代碼都寫在裡面。
本文來自:
 

UM程式研究日誌 發表在 痞客邦 留言(0) 人氣()

呵呵,所以這一節我們就來設計下界面和最基本讀取人物其他信息。
我們的目的把界面設計成如下圖片所示的樣子,當然怎麼排列還是看自己喜歡,呵呵。
  
  

一個按鈕,一個時鐘控件,其他是標籤,按著一定的排列畫在口上。
按鈕可以在上方 Standard 欄找到,時鐘控件可以在 
System 
欄找到。
我們利時鐘控件,就可以實現循環讀取遊戲的數據了,這一次的代碼將會和上一次的有點改變。
下面我們開始寫代碼:

首先,選擇時鐘控件,將 
Interval 屬設置為 100 ,意思就是1秒鐘讀取10次數據。Enabled 屬性設置為False 

雙擊按鈕,進入代碼界面,寫下如下代碼

    FirstAdr= 0x930014;
  
  GameFormHwnd=FindWindow("QElementClient Window", "Element 
Client");
    if(!GameFormHwnd)
    {
  
      ShowMessage("遊戲沒有啟動,請先啟動遊戲並完全進入遊戲!");
  
      return ;
    }
    
GetWindowThreadProcessId(GameFormHwnd,&hProcId);
    
Timer1->Enabled=true;

和上一次的差別是,我們把讀取數據的代碼放到時鐘控件裡,這裡只做遊戲的開啟判斷和數據初始化。

然後雙擊時鐘控件,由於所有的數據都在時鐘控件裡面讀取,所以代碼有點

HANDLE 
mProcID;
    DWORD eax,ecx, mex,tmp1,tmp2;
    
AnsiString ms; //byte dcode;
    WCHAR mname[64];
    
mProcID = OpenProcess(PROCESS_ALL_ACCESS, false, hProcId);
    
ReadProcessMemory(mProcID, LPVOID(0x930014), &eax, 4, 0);
    
ReadProcessMemory(mProcID, LPVOID(eax + 0x1C), &ecx, 4, 0);
    
ReadProcessMemory(mProcID, LPVOID(ecx + 0x24), &eax, 4, 0);
    
ReadProcessMemory(mProcID, LPVOID(eax + 0x25C), &HP, 4, 0);
    
ReadProcessMemory(mProcID, LPVOID(eax + 0x250), &ManLv, 4, 0);    
//0x24C 人物等級
    ReadProcessMemory(mProcID, LPVOID(eax + 0x398), 
&mex, 4, 0);   //怪物名字
    ReadProcessMemory(mProcID, 
LPVOID(mex), mname, 64, 0);
    ManName=mname;
    
ReadProcessMemory( mProcID, LPVOID(eax + 0x244), &ManID, 4, 0);  
    //人物 ID
    ReadProcessMemory(mProcID, 
LPVOID(eax + 0x25C), &HP, 4, 0);        //596 
當前體力  紅
    ReadProcessMemory(mProcID, LPVOID(eax + 
0x260), &MP, 4, 0);        //600 
當前內力  藍
    ReadProcessMemory(mProcID, LPVOID(eax + 
0x274), &MaxHP, 4, 0);     //620 最大血值
    
ReadProcessMemory(mProcID, LPVOID(eax + 0x278), &MaxMP, 4, 0);  
   //624 最大內力值
    ReadProcessMemory(mProcID, 
LPVOID(eax + 0x2DC), &Money, 4, 0);     //724 
當前錢值
    ReadProcessMemory(mProcID, LPVOID(eax + 0x7D4), 
&GWID, 4, 0);    //120版 偏移加 8 或地址 0x0354AF44 當前目標怪物ID 
,為負就是怪,為正就是NPC或玩,為0則怪物死亡,或沒有選擇怪物.
    ReadProcessMemory(mProcID, 
LPVOID(eax + 0x264), &Exp, 4, 0);     //0x25C 
當前經驗值,十進制
    ReadProcessMemory( mProcID, LPVOID(eax + 0x3C), 
&Mx, 4, 0);      //人物坐標 x  float 4字節
  
  ReadProcessMemory( mProcID, LPVOID(eax + 0x44), &My, 4, 0);  
    //人物坐標 y  float 4字節
    
//怪物信息
    
//---------------------------------------------------------
    if( 
GWID > 0x80000000)    //大於0x80000000為怪物或NPC
    
{
        WCHAR Buff[64];
  
      ReadProcessMemory( mProcID, LPVOID(FirstAdr), 
&eax, 4, 0);
        ReadProcessMemory( 
mProcID, LPVOID(eax + 0x1C), &ecx, 4, 0);
     
   ReadProcessMemory( mProcID, LPVOID(ecx + 0x8), &eax, 4, 
0);
        ReadProcessMemory( mProcID, 
LPVOID(eax + 0x20), &ecx, 4, 0);
     
   ReadProcessMemory( mProcID, LPVOID(ecx + 0x24), &tmp1, 4, 
0);  //讀出怪物數組的最大值
     
   ReadProcessMemory( mProcID, LPVOID(ecx + 0x18), &tmp2, 4, 
0);  //讀出怪物列的首地址
        if(GWID!=0) 
eax = tmp2 + (GWID % tmp1) * 0x4;
     
   ReadProcessMemory( mProcID, LPVOID(eax), &ecx, 4, 
0);
        ReadProcessMemory( mProcID, 
LPVOID(ecx + 0x4), &eax, 4, 0);
     
   ReadProcessMemory( mProcID, LPVOID(eax + 0x134), &GWHP, 4, 
0); //怪物當前血值
        ReadProcessMemory( 
mProcID, LPVOID(eax + 0x14C), &GWMaxHP, 4, 0); //怪物血值上限
  
      //ReadProcessMemory( mProcID, LPVOID(eax + 0xB4), 
&GWType, 4, 0);    //選類型
     
   //ReadProcessMemory( mProcID, LPVOID(eax + 0x120), 
&GWTypeID, 4, 0); //怪物類
     
   ReadProcessMemory( mProcID, LPVOID(eax + 0x128), &GWLv, 2, 
0);
        ReadProcessMemory( mProcID, 
LPVOID(eax + 0x3C), &GWMx, 4, 0);   //怪物X坐標
     
   ReadProcessMemory( mProcID, LPVOID(eax + 0x44), &GWMy, 4, 
0);   //怪物Y坐標
     
   ReadProcessMemory( mProcID, LPVOID(eax + 0x234), &MtoG, 4, 
0);   //怪物距離
     
   //怪物名稱部分------------------------------------
  
      ReadProcessMemory( mProcID, LPVOID(eax + 0x210), 
&ecx, 4, 0);   //怪物名字
     
   ReadProcessMemory( mProcID, LPVOID(ecx), Buff, 64, 
0);
        GWName = Buff;
  
      
    }
    else if(GWID 
< 0x80000000 && GWID >0)    //玩家
    
{
        WCHAR Buff2[64];
  
      ReadProcessMemory( mProcID, LPVOID(FirstAdr), 
&eax, 4, 0);
        ReadProcessMemory( 
mProcID, LPVOID(eax + 0x1C), &ecx, 4, 0);
     
   ReadProcessMemory( mProcID, LPVOID(ecx + 0x24), &eax, 4, 
0);
     
   //-------------------------------------------------------
  
      ReadProcessMemory( mProcID, LPVOID(eax + 0x138), 
&ecx, 4, 0);
     
   //-------------------------------------------------------
  
      ReadProcessMemory( mProcID, LPVOID(ecx + 0x24), 
&tmp1, 4, 0);  //讀出怪物數組的最大值
     
   ReadProcessMemory( mProcID, LPVOID(ecx + 0x18), &tmp2, 4, 
0);  //讀出怪物列表的首地址
        if(GWID!=0) 
eax = tmp2 + (GWID % tmp1) * 0x4;
     
   ReadProcessMemory( mProcID, LPVOID(eax), &ecx, 4, 
0);
        ReadProcessMemory( mProcID, 
LPVOID(ecx + 0x4), &eax, 4, 0);
     
   ReadProcessMemory( mProcID, LPVOID(eax + 0x25C), &GWHP, 4, 
0); //選中人物當前血值
        ReadProcessMemory( 
mProcID, LPVOID(eax + 0x274), &GWMaxHP, 4, 0); //選中人物血值上限
  
      ReadProcessMemory( mProcID, LPVOID(eax + 0x250), 
&GWLv, 2, 0);
        //ReadProcessMemory( 
mProcID, LPVOID(eax + 0xB4), &GWType, 4, 0);  //選中類型
  
      ReadProcessMemory( mProcID, LPVOID(eax + 0x3C), 
&GWMx, 4, 0);   //怪物X坐標
     
   ReadProcessMemory( mProcID, LPVOID(eax + 0x44), &GWMy, 4, 
0);   //怪物Y坐標
     
   ReadProcessMemory( mProcID, LPVOID(eax + 0x4D0), &MtoG, 4, 
0);    //人與人距離
        if( GWID == 
ManID && GWID!=0)
        {
  
          GWHP = HP;
     
       GWMaxHP = MaxHP;
     
       GWMx = Mx;
        
    GWMy = My;
     
   }
     
   //選擇人物名字部分----------------------------------------
  
      ReadProcessMemory( mProcID, LPVOID(eax + 0x398), 
&ecx, 4, 0);
        ReadProcessMemory( 
mProcID, LPVOID(ecx), Buff2, 64, 0);
     
   GWName = Buff2;
    }
    
//下面開始顯示數據
    LbName->Caption="人物: " + ManName;
  
  LbLv->Caption = "等級: " + AnsiString(ManLv);
    
LbId->Caption = "ID: " + AnsiString(ManID);
    
LbManPos->Caption = "坐標: X: " + ms.FormatFloat("0.0",Mx) + "   Y: " 
+
    ms.FormatFloat("0.0",My);
    LbHP->Caption= 
"HP: " + IntToStr(HP) + " / " + IntToStr(MaxHP);
    
LbMP->Caption= "MP: " + IntToStr(MP) + " / " + IntToStr(MaxMP);
  
  LbExp->Caption = "Exp: " + IntToStr(Exp);
    
LbMon->Caption = "金錢: " + AnsiString(Money / 10000) + "金 " 
+  AnsiString((Money % 10000) / 100) +
    "銀 " + 
AnsiString(Money % 100) +    "銅";
    if( GWID > 
0)
    {
        if( GWID == 
ManID)
           
Form1->LbObj->Caption = "目標: " + AnsiString(ManLv) + "級 ID:" 
+
            AnsiString(ManID) + " " 
+ ManName;
        else
     
       Form1->LbObj->Caption = "目標: " + 
AnsiString(GWLv) + "級 ID:" +
           
AnsiString(GWID) + " " + GWName;
    }
    
else
        Form1->LbObj->Caption = "目標: 
";
    LbObjHP->Caption="目標HP: " + IntToStr(GWHP) + " / " + 
IntToStr(GWMaxHP);
    LbObjPos->Caption = "坐標: X: " + 
ms.FormatFloat("0.0",GWMx) + "   Y: " + ms.FormatFloat("0.0",GWMy) + 
"   距離: " + ms.FormatFloat("0.0",MtoG);
    
//顯示段完
    
CloseHandle(mProcID);

以上就是在時鐘控件裡的代碼。
另外,我們需要定義一些全局變量,在 


#include <vcl.h>
#pragma hdrstop
#include 
"Unit1.h"
//---------------------------------------------------------------------------
#pragma 
package(smart_init)
#pragma resource "*.dfm"
TForm1 
*Form1;

這些代碼下面,加入以下代碼

DWORD FirstAdr;
DWORD 
ShutTime,hProcId,ManID,HP,MaxHP,MP,MaxMP,Money;
DWORD 
GWID,GWMaxHP,GWHP,ObjHP,ObjMaxHP,ObjID,Exp,MaxExp,ExpLvUp;
HWND 
GameFormHwnd;
long ManLv,GWLv;
float 
MtoG,AreaMtoG,GWMx,GWMy,Mx,My;
AnsiString 
ManName,GWName;


到這裡,完成讀取基本信息的製作。
 

UM程式研究日誌 發表在 痞客邦 留言(0) 人氣()

環境: Windows XP SP2   ,Borland C++ Builder 平台下編譯通過
對像目標: 
網絡遊戲  武林外傳 

上一節只說到從顏色方面入做掛,這一節開始講從分析遊戲內存做掛。
武林外傳遊戲進程 
elementclient.exe  沒有加殼,這對我們分析有很大好處。可以CE分析遊戲內存,也可以用OD 調試遊戲。
CE 全名 
Cheat Engine  ,是一款強大的分析遊戲內存的<SPAN class="t_tag" onclick=tagshow(event) 
href="tag.php?name=%A4u%A8%E3">工具,當然也有其他工具,不過我只熟悉這個,呵呵。
OD 全名 
Ollydbg  ,一款調試工具。我們用它可以分析遊戲的Call 
和一些偏移等。

CE的用法,大可以到廣海遊戲上看詳細教程,這裡就不貼上了,地址是
http://www.ghoffice.com/bbs/thre ... 
sc-DESC-page-1.html

其實廣海上面有很<SPAN class="t_tag" 
onclick=tagshow(event) href="tag.php?name=%B0%AA%A4%E2">高手的帖子和一些<SPAN 
class=t_tag onclick=tagshow(event) 
href="tag.php?name=%B8g%C5%E7">經驗方法帖,還有很多教程帖,<SPAN class="t_tag" 
onclick=tagshow(event) 
href="tag.php?name=%B7s%A4%E2">新手們可以從那裡起步學習基礎分析與製作,告訴大家哈,我也是從那裡開始的,呵呵。
為我們這裡只是講外掛製作,分析的地方很少,所以大部分的分析,我只是簡單的帶過,如需要詳細的<SPAN 
class=t_tag onclick=tagshow(event) 
href="tag.php?name=%B8%EA%AE%C6">資料,可以到廣海上搜索一下,很多的。

如何設計一個外掛,對很多新手想必是一個頭疼的問題,除了一開始要大量的分析外,程序製作部分也是一個難關。那麼多編譯工具,該選擇哪一款呢,這裡,我推薦初學者選擇VB 
,有一定基礎的可以直接選擇C++ ,其Borland C++ Builder 就是一個不錯的編譯工具。使用方法和VB一樣簡單。而VC++ 
則是需要熟悉界面的製作才推薦選擇。VB 版的武林助手源碼以前發過,這裡就不再發了,我們直接用C++ 
開始製作新版的掛,也就是武林小秘。

假設前提我們已經得到了足夠的信息和資料,例如基址,偏移等。
那麼我們開始來試著讀取一個偏移地址,例如<SPAN 
class=t_tag onclick=tagshow(event) 
href="tag.php?name=%A4H%AA%AB">人物的血值。
讀取武林172版人物血值,首先打開BCB,開啟後都會<SPAN 
class=t_tag onclick=tagshow(event) 
href="tag.php?name=%A6%DB%B0%CA">自動建立一個新的工程,我們就不新建了,在控件欄出選擇按鈕,在口裡畫出一個合適大小的按鈕來,再選擇Lable控件,照樣畫在窗口上,雙擊按鈕,進入按鈕<SPAN 
class=t_tag onclick=tagshow(event) 
href="tag.php?name=%A5N%BDX">代碼部分。
想要讀取其他進程的信息,我們首先要獲得這個進程的ID,然後打開進程,獲取信息,最後關閉打開的進程句柄。
這裡假設遊戲的基址是 
0x930014  ,這個是BCB裡十六進制的寫法,以後都會這樣寫。
一級偏移是 0x24 ,人物當前血值的偏移是 0x25C 
,下面開始寫代碼
在按鈕的代碼部分寫如下代碼:

DWORD eax,ecx,HP;
HANDLE mProcID;
DWORD 
hProcId;
HWND GameFormHwnd;
GameFormHwnd=FindWindow("QElementClient 
Window", "Element Client");

    
GetWindowThreadProcessId(GameFormHwnd,hProcId);
    
  
  mProcID = OpenProcess(PROCESS_ALL_ACCESS, false, hProcId);
  
  ReadProcessMemory(mProcID, LPVOID(0x930014), eax, 4, 0);
    
ReadProcessMemory(mProcID, LPVOID(eax + 0x1C), ecx, 4, 0);
    
ReadProcessMemory(mProcID, LPVOID(ecx + 0x24), eax, 4, 0);
    
ReadProcessMemory(mProcID, LPVOID(eax + 0x25C), HP, 4, 0);
    
Label1->Caption= IntToStr(HP);

    
CloseHandle(mProcID);

到這裡,啟動程序,就可以讀取人物當前的血值了。
下面對上面的代碼進行解釋:

DWORD 
eax,ecx,HP;  定義DWORD類型的變量,就是無符號整形變量。

HANDLE 
mProcID;  定義HANDLE 類型的句柄變量

HWND GameFormHwnd;   定義 
HWND 類型窗口句柄變量

GameFormHwnd=FindWindow("QElementClient Window", "Element 
Client"); 
利用API函數  FindWindow 我們可以得到遊戲窗口的句柄,QElementClient Window 
是類名,Element Client 是窗口標題 


GetWindowThreadProcessId(GameFormHwnd,hProcId);  進一步獲得進程Pid 
。這個很重要,我們是對進程操作,所以必須要得到進程Pid 。

mProcID = OpenProcess(PROCESS_ALL_ACCESS, 
false, 
hProcId);
這裡是打開進程,我們操作其他進程或者和其他進程通信,都必須先打開這個進程,這是Windows的死規定。

ReadProcessMemory(mProcID, 
LPVOID(0x930014), eax, 4, 0);
    ReadProcessMemory(mProcID, 
LPVOID(eax + 0x1C), ecx, 4, 0);
    ReadProcessMemory(mProcID, 
LPVOID(ecx + 0x24), eax, 4, 0);
    ReadProcessMemory(mProcID, 
LPVOID(eax + 0x25C), HP, 4, 0);
這裡就是開始讀取內存的地方了,利用ReadProcessMemory  
API函數讀取遊戲進程的內容。從上至下就是指針的順序  
基址>一級偏移>級偏移>人物血值偏移

Label1->Caption= 
IntToStr(HP);   把獲得的值 HP 
轉換成字符類型,並賦值給Lable1的Caption屬

CloseHandle(mProcID);  
關閉打開的進程,有打開必有關閉,不然會造成資源浪費,後果很嚴重。

上面紅色部分,就是我們需要獲取信息的部分,在下面還可以獲取更多的信息,例如:
ReadProcessMemory(mProcID, 
LPVOID(eax + 0x250), ManLv, 4, 0);    //0x24C 人物<SPAN class="t_tag" 
onclick=tagshow(event) href="tag.php?name=%B5%A5%AF%C5">等級
  
  ReadProcessMemory(mProcID, LPVOID(eax + 0x398), mex, 4, 0);  
//<SPAN class="t_tag" onclick=tagshow(event) 
href="tag.php?name=%A9%C7%AA%AB">怪物名字偏移
    
ReadProcessMemory(mProcID, LPVOID(mex), mname, 64, 0);    
//存放字符的地址,讀取後就是怪物的名字,(注意mex 變量需要先定義)
    ReadProcessMemory(mProcID, 
LPVOID(eax + 0x25C), HP, 4, 0);        //596 
當前體力  紅
    ReadProcessMemory(mProcID, LPVOID(eax + 
0x260), MP, 4, 0);        //600 
當前內力  藍
    ReadProcessMemory(mProcID, LPVOID(eax + 
0x274), MaxHP, 4, 0);     //620 最大血值
    
ReadProcessMemory(mProcID, LPVOID(eax + 0x278), MaxMP, 4, 0);  
   //624 最大內力值
    ReadProcessMemory(mProcID, 
LPVOID(eax + 0x2DC), Money, 4, 0);     //724 當前錢值
  
  ReadProcessMemory(mProcID, LPVOID(eax + 0x7D4), GWID, 4, 0);    
//120版 偏移加 8 或地址 0x0354AF44 當前目標怪物ID ,為負就是怪,為正就是NPC或<SPAN class="t_tag" 
onclick=tagshow(event) 
href="tag.php?name=%AA%B1%AEa">玩家,為0則怪物死亡,或沒有選擇怪物.
    
ReadProcessMemory(mProcID, LPVOID(eax + 0x264), Exp, 4, 0);  
   //0x25C 當前經驗值,十進制
    ReadProcessMemory(mProcID, 
LPVOID(eax + 0x2D8), GJDis, 4, 0);
    //升級經驗值是一個地址 加 
人物等級*4
    ReadProcessMemory( mProcID, LPVOID(MaxExpAdr + ManLv * 
0x4), MaxExp, 4, 0);      //0x930 人物升級經驗值,十進制(MaxExpAdr 
需要另外分析尋找)
    //ReadProcessMemory( mProcID, LPVOID(0x6020630), 
MaxExp, 4, 0);
    ReadProcessMemory( mProcID, LPVOID(eax + 0x3C), 
Mx, 4, 0);      //人物坐標 x  float 4字節
  
  ReadProcessMemory( mProcID, LPVOID(eax + 0x44), My, 4, 0);  
    //人物坐標 y  float 
4字節
這裡是一些基本的人物信息,在BCB裡面讀取遊戲內存,一般就是這個模式: 
找到需要處理的窗口的句柄,獲取該句柄指向的進程Pid,打開進程,讀取信息,關閉打開的進程。

這裡要說下,LPVOID(eax + 
0x44)  這個地方必須進程強制類型轉換,不然可能會出錯而無法編譯。LPVOID 的意思就是強制轉換為無類型指針 
類型。

一個Lable 標籤可以對應一個<SPAN class="t_tag" onclick=tagshow(event) 
href="tag.php?name=%BC%C6%BE%DA">數據顯示,當然也可以都顯示在一個標籤裡,不過不推薦,有多少個數據需要顯示,就應該每行畫出一個Lable標籤,格式上看起來都要舒服很多。

凡是都要知道,變量需要先定義後使用,如果你把上面這些代碼直接複製到程序裡,是行不通的,呵呵。


這一節到此結束,下一節我們進行界面設計和其他信息處理。
 

UM程式研究日誌 發表在 痞客邦 留言(0) 人氣()