这半年来,由于女王将我的台式机占用,只能用可怜的macbook来看看网页之类的。因为 macbook比较老,只有2G内存,在windows上只分了20G的磁盘空间,随便装了几个程序,便 没有空间了。
最近在分析一个程序,正好用到了比如补丁,hook之类的,在很早很早之前的程序中,我是 用C++ Builder来做的,但苦于没有空间,也不想去找一个破解的程序了。就决定还是用 mingw+原生的windows api来做吧。
闲话说完了,开始上代码:
编译的方法
CC_PRE = mingw32- AS=$(CC_PRE)as CC=$(CC_PRE)gcc CPP:=$(CC_PRE)g++ LD=$(CC_PRE)gcc RES=windres ASFLAGS=-g CCFLAGS=-g -D _WIN32_IE=0x0500 -D WINVER=0x500 CPPFLAGS=$(CCFLAGS) LDFLAGS= -lcomdlg32 -lgdi32 -luser32 -Wl,--subsystem,windows hook.dll OBJECT=resource.o main.o TARGET=run.exe all: ready $(TARGET) clean: rm -rf *.o *.res *.exe ready: cd ../dll && make $(TARGET): $(OBJECT) $(LD) $(OBJECT) -o $(TARGET) $(LDFLAGS) %.o: %.cpp $(CPP) $(CPPFLAGS) -c -o $@ $< %.o: %.c $(CC) $(CCFLAGS) -c -o $@ $< resource.o: resource.rc resource.h $(RES) -i $< -o $@ %.o: %.s $(AS) $(ASFLAGS) -o $@ $<
上面的是个很简单的exe的编译脚本,dll和上面的类似,只是LDFLAGS一行换成: LDFLAGS= -lcomdlg32 -lgdi32 -luser32 -Wl,–subsystem,windows -shared -Wl,–out-implib,libhook_dll.a
resource我是使用resedit编辑的,上面只有两个按钮,hook和open,
{ PUSHBUTTON “hook”, IDC_BUTTON2, 73, 7, 50, 14, 0, WS_EX_LEFT PUSHBUTTON “open”, IDC_BUTTON1, 15, 9, 40, 11, 0, WS_EX_LEFT }
接下来是exe程序,也很简单的:
#include <windows.h> #include "resource.h" /* * dll中的函数,打开hook窗口 */ extern "C" __declspec(dllimport) bool EnableHook(DWORD dwThreadId, char* afilename ); /* * dll中的函数,关闭hook */ extern "C" __declspec(dllimport) bool DisableHook(); /* * 从文件全路径中获取文件名之外的部分,即文件所在的目录,这里简单写个函数 */ char* ExtractFileDir(char* filename) { static char szFile[MAX_PATH]; strcpy(szFile, filename); char* pos = szFile+ (strlen(szFile) -1); while(pos> szFile) { if (*pos == '\\' || *pos == '/' || *pos == ':') { *pos = 0; break; } pos--; } return szFile; } /* * 结构体,用来管理不同更能的补丁 */ typedef struct { byte* buf; //!< 补丁的地址 int size; //!< 补丁的字节长度 void* addr; //!< 补丁的位置 } TPatch; byte no_check_buf[] = {0xeb,}; //!< 补丁例子1的补丁内容 TPatch no_check = { no_check_buf, sizeof(no_check_buf)/sizeof(no_check_buf[1]), (void*)0x0046bb1e }; //!< 补丁例子1的结构填充 TPatch no_check_2 = { no_check_buf, sizeof(no_check_buf)/sizeof(no_check_buf[1]), (void*)0x0046c4f8 }; //!< 补丁例子2的结构填充 TPatch* patch[] = { &no_check, &no_check_2 }; //!< 这样就可以简单的弄个补丁的数组了 /* * 打开进程, 同时, 在打开进程的时候,对进程进行内存打补丁操作 */ BOOL OpenProcess(char* filename, HANDLE& process_handle, DWORD& process_threadhandle) { if (filename == 0) return FALSE; PROCESS_INFORMATION pi; STARTUPINFO si; si.cb=sizeof(si); si.lpReserved=NULL; si.lpDesktop=NULL; si.lpTitle=NULL; si.cbReserved2=0; si.lpReserved2=NULL; si.dwFlags=STARTF_USEPOSITION; si.dwX=0; si.dwY=0; if (CreateProcess(filename, NULL, NULL, NULL, FALSE, \ CREATE_SUSPENDED, NULL, ExtractFileDir(filename), \ &si, &pi)) { process_handle = pi.hProcess; process_threadhandle = pi.dwThreadId; for (int i = 0; i < sizeof(patch)/sizeof(patch[1]); i++) { if(WriteProcessMemory(process_handle,patch[i]->addr, \ patch[i]->buf,patch[i]->size, \ NULL)==false ) { char error[2] = {0}; MessageBox( NULL, "cann't write", "error", 0); } } ResumeThread(pi.hThread); return TRUE; } return FALSE; } /* * 打开文件对话框 */ char* OpenFileDialog(HWND hwnd) { static char szFile[MAX_PATH]; OPENFILENAME ofn; memset(szFile, 0, sizeof(szFile)); memset(&ofn, 0, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.lpstrFile = szFile; ofn.hwndOwner = hwnd; ofn.nMaxFile = sizeof(ofn); ofn.lpstrFilter = ("Demo.exe\0*.exe\0All files(*.*)\0*.*\0"); ofn.lpstrFile[1] = 0; if(GetOpenFileName((LPOPENFILENAME)&ofn)) { return szFile; } else { return 0; } } HANDLE process_handle = 0; DWORD thread_handle = 0; /* * 主exe的消息循环 */ BOOL CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_CREATE: //MessageBox(hwnd, "Hello", "fdas", 0); break; case WM_COMMAND: switch(LOWORD(wParam)) { case IDC_BUTTON1: { //MessageBox(hwnd, "press", "test", 0); if (OpenProcess(OpenFileDialog(hwnd), process_handle, thread_handle) == true) { } } break; case IDC_BUTTON2: { //MessageBox(hwnd, "press", "button", 0); if (thread_handle == 0) { MessageBox(hwnd, "press", "button ==0", 0); } else { EnableHook(thread_handle, "test"); } } break; } break; case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; /*default: return DefWindowProc(hwnd, uMsg, wParam, lParam);*/ } return 0; } /* * 入口函数 */ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmd, int nCmd) { DialogBox(hInst, MAKEINTRESOURCE(IDD_DLG1), NULL, WndProc); return 0; }
我尽量写的直观简单,上述代码只做两件事情,打开进程和打开hook功能,接下来是 hook.dll的代码:
#include "windows.h" #include "stdio.h" #include "resource.h" HHOOK g_hHook = NULL; //hook句柄 HINSTANCE DllHinst = NULL; //dll实例 HWND gamehWnd; //进程handle HANDLE hThread = NULL; //!< 线程handle,用于运行dll中的对话框 HWND wghandle = NULL; HANDLE gamehandle; HINSTANCE gameInstance; DWORD ThreadID; LRESULT CALLBACK KeyboardHook(int nCode, WPARAM wParam, LPARAM lParam); /* * 导出函数,开启hook */ extern "C" __declspec(dllexport)bool EnableHook(DWORD dwThreadId, char* afilename ); /* * 导出函数,关闭hook */ extern "C" __declspec(dllexport)bool DisableHook(); /* * dll入口函数,标准dll程序 */ extern "C" BOOL WINAPI DllMain(HINSTANCE hinst, unsigned long reason, void* lpReserved) { DllHinst = hinst; return 1; } extern "C" __declspec(dllexport)bool EnableHook(DWORD dwThreadId, char* afilename ) { if (g_hHook == NULL) { g_hHook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)KeyboardHook, DllHinst, dwThreadId); } if (g_hHook) { return true; } char temp[3] = {0}; sprintf(temp, "%d:%d:%d", GetLastError(), DllHinst, dwThreadId); MessageBox(NULL, "error", temp, 0); return false; } extern "C" __declspec(dllexport)bool DisableHook() { if (g_hHook != NULL) { UnhookWindowsHookEx(g_hHook); g_hHook = NULL; return true; } return false; } BOOL CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_CREATE: //MessageBox(hwnd, "Hello", "fdas", 0); break; case WM_COMMAND: switch(LOWORD(wParam)) { case IDC_BUTTON1: { MessageBox(hwnd, "press in dll", "test", 0); } break; case IDC_BUTTON2: { MessageBox(hwnd, "press in dll", "button", 0); } break; } break; case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; /*default: return DefWindowProc(hwnd, uMsg, wParam, lParam);*/ } return 0; } /* * 对话框线程 */ DWORD WINAPI Thread1(PVOID param) { DialogBox(DllHinst, MAKEINTRESOURCE(IDD_DLG1), NULL, WndProc); return 1; } /* * 键盘hook回调,123是F12,因此,按下F12后,会弹出dll中的对话框,这个对话框和想要注入的进程是在同一个进程中。可以在这里面对进程做任何事情 */ LRESULT CALLBACK KeyboardHook(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode >= 0) { if (wParam == 123) { if (hThread == NULL) { //MessageBox(NULL, "press2", "button", 0); gamehWnd = GetActiveWindow(); gamehandle =GetCurrentProcess(); gameInstance = (HINSTANCE)GetWindowLong(gamehWnd, GWL_HINSTANCE); hThread = CreateThread(NULL, 0, Thread1, NULL, NULL, &ThreadID); } } } return (CallNextHookEx(g_hHook, nCode, wParam, lParam)); }
主要内容已经注释清楚了,代码也是尽可能精简。上述的dll中包含了一个对话框,我用的 是和exe相同的对话框,因此,当成功之后,会弹出两个一样的对话框,不过,点了dll中的 哪个,会弹出上面的messagebox中的提示内容。
dll由于是在目标进程的进程空间中,因此,可以调用目标进程中的任意函数,访问任意内 存变量,可以通过这个dll对目标进程进行任何方便的事情。dll中的对话框是运行在独立的 线程中的,因此,dll中对话框的任何显示并不会对原来的进程造成阻塞或者干扰。
这样编译出来的dll和exe都非常小,按照我的哪个makefile脚本,两个加起来大约100KB左 右,如果去掉调试的-g,并添加-O2或者-O3的话,可能还会更小,而且,仅依赖系统的dll, 不用什么其他的附加库。
上述程序编译出来的exe和dll,在xp上可以直接运行,在win vista/7上,需要使用管理员 权限运行exe,不然的话,无权限对进程进行操作,谁有兴趣的话,可以在exe里面做个提权 的操作。