【前言】
夜里失眠,到這個論壇上泡,發(fā)現(xiàn)這個編程版塊不是很正規(guī),因為我認(rèn)為的好的編程版塊應(yīng)該是以討論分享及解決具體問題而存在的,而不是想咱論壇這樣:
救助的問題不明確,分享就是單純的分享源碼,分享XX成品之類……
【廢話】
前幾天研究外掛了,就我這點破水平,脫機啊,封包掛啊之類的與我無緣了,只能做做內(nèi)掛,滿足一下自己的虛榮心……
這里大概的給大家分享一下做內(nèi)掛編程方面的知識,高手飄過,跟我一樣菜的朋友多多提出您的寶貴意見,相互學(xué)習(xí)、共勉!!!
當(dāng)然了,也希望這篇文章能騙個精華之類的東東,作為我炫耀的資本~~~~
內(nèi)掛,其實不難的,這里主要分享遠程線程注入相關(guān)的東西,至于其它什么游戲找CALL,功能CALL調(diào)用相關(guān)的東西,可以參考一下我寫的:
賺取權(quán)限第三貼,一個有意思的keygenMe的逆向分析 其實道理都是相同的,當(dāng)然,如果您非常想要了解相關(guān)的東西,請參考別人大牛寫的文章,這里不做詳細(xì)討論。
【基礎(chǔ)】
我理解的遠程線程注入,包括注入代碼,注入DLL分為這兩種(如果有不對的地方希望指正,謝謝),注入DLL就不多廢話了,就是把DLL及所在目錄的名字一些通過寫內(nèi)存的API寫到目標(biāo)進程中,我今天主要討論的是向目標(biāo)進程寫代碼,是獲取目標(biāo)進程的句柄的方法有很多的,比如創(chuàng)建進程鏡像,然后遍歷進程,再如通過遍歷窗口標(biāo)題,類名之類得到窗口句柄,然后在由窗口句柄得到進程的句柄,這里只舉最常用到的通過窗口名的方法,至于遍歷進程的方法有很多的,您可以百度一下就能找到……
void CZHGameDlg::OnShuaxin()
{
//載入游戲
DWORD dwZHThreadId;
DWORD dwZHProcessId;
HWND hZHWnd;
DWORD BASE;
char tmpName[65];
int i = 0;
CString sClassName= "";
hZHWnd = ::FindWindow("縱橫時空",NULL);
UpdateData(TRUE);
m_nChioceGM.ResetContent();
if(hZHWnd != NULL)
{
while(hZHWnd > 0)
{
if(i >=5)
{
::AfxMessageBox("因掛機效率考慮,本掛最多可以控制5個角色!請關(guān)閉一些角色然后點刷新按鈕!");
break;
}
int hFunc=GetClassName(hZHWnd,sClassName.GetBuffer(0),2000);
if(hFunc != 0 && 0 <= sClassName.Find("縱橫時空"))
{
if( dwZHThreadId = ::GetWindowThreadProcessId(hZHWnd, &dwZHProcessId))
{
try
{
m_hZHProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwZHProcessId+1);
if(m_hZHProcess != NULL)
{
ReadProcessMemory(m_hZHProcess,(LPVOID)ZH_BASE_ADDR,&BASE,4,NULL);
ReadProcessMemory(m_hZHProcess,(LPVOID)(BASE+0xAC),tmpName,20,NULL);
ProcName[i].Name = tmpName;
ProcName[i].hZHhwnd = hZHWnd;
ProcName[i].hZHProcess = m_hZHProcess;
if( 0> m_nChioceGM.FindStringExact(0,ProcName[i].Name))
m_nChioceGM.AddString(ProcName[i].Name);
SetDlgItemText(IDC_TISHI,"提示: 遍歷游戲角色完畢,請選擇游戲角色
");
i++;
UpdateData(FALSE);
}
}catch(
)
{
::AfxMessageBox("出現(xiàn)未知錯誤");
}
}
}
hZHWnd = ::GetNextWindow(hZHWnd,GW_HWNDNEXT);
}
}
else
{
SetDlgItemText(IDC_TISHI,"提示: 沒有找到游戲窗口,請確認(rèn)游戲是否已經(jīng)開啟
");
}
}
這樣我們就可以得到同一個游戲的所有窗口句柄、進程句柄,游戲角色名等信息(這里只以縱橫時空這個游戲來說,由于不同的游戲,游戲數(shù)據(jù)的存儲用的數(shù)據(jù)結(jié)構(gòu)各不相同,所以,這個取角色名的位置不能通用!)如果你有其它的方法,歡迎跟帖子分享!
【內(nèi)容一:簡單的代碼執(zhí)行】
到這里,我們已經(jīng)得到目標(biāo)窗口的句柄了,然后接下來就是調(diào)用游戲中的函數(shù),讓游戲去執(zhí)行它,以達到我們的通過程序控制的目的!
比如,我先要讓我們的程序能夠控制游戲,比如讓我們程序控制的角色簡單的跳一下。假設(shè)我們通過分析游戲的代碼,找到,跳,這個動作的代碼如下:
//--------->跳<---------//
DWORD _stdcall JumpA()
{
DWORD RealAddress,Addr;
Sell_STR SellParam;
RealAddress = 0x00409C80; //真正CALL地址
Addr = 0x004336F0; //加密CALL地址
_asm
{
push 0
mov eax, dword ptr [0x00833C5C] //游戲基址
add eax, 108
push eax
lea ecx,SellParam
call Addr
push eax
MOV ecx, 0x0084B908
call RealAddress
}
return 0;
}
只要我們能讓游戲順利的執(zhí)行我們的上述代碼,我們就可以通過傳入不同的參數(shù),來達到拾取物品的目的!
怎么樣來讓游戲執(zhí)行這個代碼呢?
游戲的進程跟我們自己的進程是兩個獨立的實例,自然的,游戲不會跑到我們的進程空間內(nèi)來執(zhí)行我們程序的代碼,否則Windows就亂套了~~~,我們要做的,就只有兩步!
第一步 : 把要讓游戲執(zhí)行的代碼寫入游戲的進程空間內(nèi)!
第二步 : 給游戲進程創(chuàng)建一個線程,讓它執(zhí)行上述的代碼!
其實要完成上述的兩步不難的,寫目標(biāo)進程的API函數(shù)是:WriteProcessMemory ,具體的定義如下:
BOOL WriteProcessMemory(
HANDLE hProcess, // handle to process whose memory is written to
LPVOID lpBaseAddress,
// address to start writing to
LPVOID lpBuffer, // pointer to buffer to write data to
DWORD nSize, // number of bytes to write
LPDWORD lpNumberOfBytesWritten
// actual number of bytes written
);
至于在目標(biāo)進程創(chuàng)建個線程的API及定義如下:
HANDLE CreateRemoteThread(
HANDLE hProcess, // handle to process to create thread in
LPSECURITY_ATTRIBUTES lpThreadAttributes, // pointer to security attributes
DWORD dwStackSize, // initial thread stack size, in bytes
LPTHREAD_START_ROUTINE lpStartAddress, // pointer to thread function
LPVOID lpParameter, // argument for new thread
DWORD dwCreationFlags, // creation flags
LPDWORD lpThreadId // pointer to returned thread identifier
);
好了,把我寫的一個遠程線程注入代碼的函數(shù)貼一下:
void InjectToProcess(DWORD (*FunName)())
{
HANDLE TmpHandle;
DWORD dwThreadId;
DWORD dwProcessId;
CString sClassName= "";
m_hPwnd = ::FindWindow("TForm1","游戲找CALL練習(xí)實例one");//得到窗口句柄
if(m_hPwnd == NULL)
{
MessageBox("沒有找到主程序,請先運行主程序");
}
int hFunc=GetClassName(m_hPwnd,sClassName.GetBuffer(0),2000);
if(hFunc != 0 && 0 <= sClassName.Find("TForm1"))
{
if( dwThreadId = ::GetWindowThreadProcessId(m_hPwnd, &dwProcessId))
{
m_hProcess = ::OpenProcess(PROCESS_ALL_ACCESS,FALSE, dwProcessId);
if(m_hProcess != NULL)
{
//在目標(biāo)進程建立內(nèi)存空間
LPVOID ThreadAdd = ::VirtualAllocEx(m_hProcess, NULL,0x1024, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
::WriteProcessMemory(m_hProcess,ThreadAdd ,FunName,0x1024,NULL);
TmpHandle = CreateRemoteThread(m_hProcess,
NULL, 0, (LPTHREAD_START_ROUTINE)ThreadAdd, NULL,
0, NULL);//獲得注入后過程的句柄ID
if(WaitForSingleObject(TmpHandle,INFINITE) != WAIT_OBJECT_0)
{
CString StrTmp;
StrTmp.Format("%d",GetLastError());
MessageBox(StrTmp);
}
CloseHandle(TmpHandle);
CloseHandle(m_hProcess);
VirtualFreeEx(m_hProcess,ThreadAdd,0x1024,MEM_RELEASE);
}
}
}
}
這樣,我們只要在函數(shù)里調(diào)用:InjectToProcess(JumpA)
就可以搞定了!
【內(nèi)容一:關(guān)于參數(shù)的傳遞】
其實,絕大多數(shù)游戲里的函數(shù)都是帶有參數(shù)的,遠程線程里參數(shù)的傳遞其實也不難的,直接舉個例子吧,希望對卡在這里的朋友有所幫助!
這里舉自動的拾取物品的例子,比如我們通過對游戲的分析,找到游戲拾取物品的代碼如下:
//--------->撿取物品<---------//_OK
void _stdcall JianWuA(PP1_STR P)
{
DWORD SendAddr = ZH_CALL_SEND;
DWORD SendEcx = ZH_CALL_ECX;
DWORD Addr = ZH_CALL_SHIQU;
DWORD WuPos;
DWORD XiangZiID;
Sell_STR SellParam;
DWORD Mubiao = ZH_SQMUBIAO_ADDR;
WuPos = P->Param1;
XiangZiID = P->Param2;
_asm
{
push WuPos //在物品欄的位置
push XiangZiID //要拾取的物品的ID
lea ecx,SellParam
Call Addr //簡單的封包加密
push eax
MOV ecx, SendEcx
call SendAddr //發(fā)包CALL
MOV BYTE PTR [Mubiao],0
}
}
這里需要的參數(shù)有兩個,一個是地上箱子的ID,一個是箱子中物品的位置,這里的遠程注入的函數(shù)當(dāng)然也不像上一個函數(shù)那么簡單了,我改成了下面的樣子:
初始化部分:
void CInjectCall::InitPorcess(HANDLE _hProcess)
{
m_hProcess = _hProcess;
if(m_hProcess != NULL)
{
//在目標(biāo)進程中為函數(shù)名創(chuàng)建空間
ThreadAdd = ::VirtualAllocEx(m_hProcess, NULL,0x128, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
//在目標(biāo)進程中為函數(shù)名創(chuàng)建空間
ParamAddr = ::VirtualAllocEx(m_hProcess, NULL,0x128, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
JiNengAddr = ::VirtualAllocEx(m_hProcess, NULL,0x128, MEM_COMMIT,PAGE_EXECUTE_READWRITE);
}
}
注入部分:
void CInjectCall::InjectFunc(LPVOID pFun, DWORD *Param, DWORD ParamSize)
{
HANDLE TmpHandle;
DWORD lpNumberOfBytes;
if(m_hProcess != NULL)
{
// ---- 寫入函數(shù)地址
::WriteProcessMemory(m_hProcess, ThreadAdd, pFun, 0x128, &lpNumberOfBytes);
// ---- 寫入?yún)?shù)地址
::WriteProcessMemory(m_hProcess, ParamAddr, Param, ParamSize, &lpNumberOfBytes);
TmpHandle = ::CreateRemoteThread(m_hProcess,
NULL, 0, (LPTHREAD_START_ROUTINE)ThreadAdd,
ParamAddr, //有參數(shù)就寫參數(shù),無參數(shù)就為NULL
0, &lpNumberOfBytes);//獲得注入后過程的句柄ID
::WaitForSingleObject(TmpHandle,INFINITE);
::CloseHandle(TmpHandle);
}
}
內(nèi)存釋放部分:
void CInjectCall::TheEndPorcess()
{
if(m_hProcess != NULL)
{
::VirtualFreeEx(m_hProcess,JiNengAddr,0x128,MEM_RELEASE);
::VirtualFreeEx(m_hProcess,ThreadAdd,0x128,MEM_RELEASE);
::VirtualFreeEx(m_hProcess,ParamAddr,0x128,MEM_RELEASE);
}
}
我們調(diào)用的方式也就變成了:
void CInjectCall::JianWu(int WuPosInList, int XiangZiID)
{
P1_STR MyParam;
DWORD ParamSum;
BYTE tmpid = 0;
MyParam.Param1 = WuPosInList;
MyParam.Param2 = XiangZiID;
ParamSum = sizeof(MyParam);
InjectFunc(JianWuA, (DWORD *)&MyParam,ParamSum);
}
P1_STR 的結(jié)構(gòu)體定義如下:
typedef struct _P1_STR{
int Param1;
int Param2;
int Param3;
}P1_STR,*PP1_STR;
這樣就可以了~~~~
【結(jié)尾】
今天要說的東西就這么多,一些沒什么技術(shù)含量的東西,希望對正需要的朋友帶來幫助,也十分的感謝大家能看完我這又臭又長的破爛文章!
感覺,不管匯編也好,Delphi也好,VB,JAVA也罷,還是C順手,極力推薦大家學(xué)C,因為很多的東西都可以自己控制,而且又簡單明了,真的是簡約而不簡單的·~~~
好了,去就說這么多,希望各位大牛不要笑話,同我一樣的菜菜繼續(xù)努力,去睡覺去了~~~~
游戲的半成品外掛連接如下:
/Files/bester/ZH_Boot/ZHPlg_src.rar