例子在这里:https://github.com/mschadev/detours-example
detours-example
API hooking example project using Microsoft Detours
Install
- Run git bash
git clone https://github.com/zxc010613/detours-example.git cd detours-example git submodule init git submodule update
- Run `Developer Command Prompt for VS 2019(or 2017)
cd Detours nmake cd .. devenv detours-example.sln /build "release|x86"
Usage
- Run detours-example.exe
- Run dll-injector.exe(Run as administrator)
我们就重点讲解下其原理:
运行效果截图:
首先,运行detours-example.exe
点击回车后,弹出对话框:
然后,管理员下:运行,dll-injector.exe,注意将test-dll.dll放在同一个目录。
可以看到弹出的对话框已经修改了!使用的就是api hook方式操作的!
最后退出以后,又恢复正常了:
好了,我们看下代码逻辑:
首先是要注入的程序,很简单就是一个无线循环弹窗的代码:
// detours-example.cpp : 이 파일에는 'main' 함수가 포함됩니다. 거기서 프로그램 실행이 시작되고 종료됩니다. // #include <iostream> #include <Windows.h> int main() { while (true) { MessageBox(GetFocus(), L"Hello world!", L"detours-example", MB_OK); std::cout << "Enter to show messagebox:"; std::cin.get(); system("cls"); } }
然后是注入的代码:基本上也没有什么依赖,就是使用test-dll.dll文件作为dll注入,注意里面有提权啊。
// dll-injector.cpp : 이 파일에는 'main' 함수가 포함됩니다. 거기서 프로그램 실행이 시작되고 종료됩니다. // #include <iostream> #include "windows.h" #include <TlHelp32.h> #include <tchar.h> BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) { TOKEN_PRIVILEGES tp; HANDLE hToken; LUID luid; // 현재 프로세스의 핸들을 가져와 관련된 액세스토큰을 가져옴. if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { printf("OpenProcessToken error: %u\n", GetLastError()); return FALSE; } // 로컬 시스템에 대한 LUID를 가져옴. if (!LookupPrivilegeValue(NULL, // lookup privilege on local system lpszPrivilege, // privilege to lookup &luid)) // receives LUID of privilege { printf("LookupPrivilegeValue error: %u\n", GetLastError()); return FALSE; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; if (bEnablePrivilege) tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; else tp.Privileges[0].Attributes = 0; // Enable the privilege or disable all privileges. if (!AdjustTokenPrivileges(hToken, // 액세스 토큰 핸들 FALSE, // TURE일 경우 모든 권한 비활성화 &tp, // TOKEN_PRIBILEGES 구조체 포인터 sizeof(TOKEN_PRIVILEGES), // 다음에 오는 버퍼의 사이즈 (PTOKEN_PRIVILEGES)NULL, // 이전 상태 없어도 됨 (PDWORD)NULL)) { printf("AdjustTokenPrivileges error: %u\n", GetLastError()); return FALSE; } if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) { printf("The token does not have the specified privilege. \n"); return FALSE; } return TRUE; } BOOL InjectDll(DWORD dwPID, LPCTSTR szDllPath) { HANDLE hProcess, hThread; LPVOID pRemoteBuf; DWORD dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR); LPTHREAD_START_ROUTINE pThreadProc; // 파라미터로 받은 프로세스의 핸들을 받아옴. if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID))) { printf("OpenProcess(%d) failed!!!\n", dwPID); return FALSE; } // 해당 프로세스의 가상메모리 공간을 할당 받음. // 대상 핸들, 할당할 메모리 번지 지정(NULL이면 시스템이 자동 지정), 할당할 메모리 양, // 할당 방법 지정, 할당한 페이지의 액세스 타입 지정 // 할당한 메모리 번지 반환 / NULL 반환 pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE); // 해당 프로세스 메모리를 조작. // 조작할 대상 프로세스 핸들, 조작할 가상메모리 주소, 메모리에 적을 값(인젝션 시킬 DLL 경로), // 메모리에 쓸 크기, 특정 프로세스의 바뀔 바이트를 받는 변수(NULL 사용 안함) WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllPath, dwBufSize, NULL); pThreadProc = (LPTHREAD_START_ROUTINE) GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW"); hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL); WaitForSingleObject(hThread, INFINITE); VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE); CloseHandle(hThread); CloseHandle(hProcess); return TRUE; } // 삽입했던 Dll 제거 // 삽입과 역순 BOOL EjectDll(DWORD dwPID, LPCTSTR szDllPath) { BOOL bMore = FALSE, bFound = FALSE; HANDLE hSnapshot, hProcess, hThread; MODULEENTRY32 me = { sizeof(me) }; LPTHREAD_START_ROUTINE pThreadProc; if (INVALID_HANDLE_VALUE == (hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID))) return FALSE; bMore = Module32First(hSnapshot, &me); for (; bMore; bMore = Module32Next(hSnapshot, &me)) { if (!_tcsicmp(me.szModule, szDllPath) || !_tcsicmp(me.szExePath, szDllPath)) { bFound = TRUE; break; } } if (!bFound) { CloseHandle(hSnapshot); return FALSE; } if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID))) { printf("OpenProcess(%d) failed!!!\n", dwPID); CloseHandle(hSnapshot); return FALSE; } pThreadProc = (LPTHREAD_START_ROUTINE) GetProcAddress(GetModuleHandle(L"kernel32.dll"), "FreeLibrary"); hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); CloseHandle(hProcess); CloseHandle(hSnapshot); return TRUE; } int main() { SetPrivilege(SE_DEBUG_NAME, TRUE); PROCESSENTRY32 entry; entry.dwSize = sizeof(PROCESSENTRY32); HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); if (Process32First(snapshot, &entry) == TRUE) { while (Process32Next(snapshot, &entry) == TRUE) { if (wcscmp(entry.szExeFile, L"detours-example.exe") == 0) { InjectDll(entry.th32ProcessID, L"test-dll.dll"); std::cout << "Enter to exit"; std::cin.get(); EjectDll(entry.th32ProcessID, L"test-dll.dll"); break; } } } CloseHandle(snapshot); return 0; }
其中main函数就是查询进程,然后注入dll,用户手动输入后可以解除注入。
SetPrivilege(SE_DEBUG_NAME, TRUE); PROCESSENTRY32 entry; entry.dwSize = sizeof(PROCESSENTRY32); HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); if (Process32First(snapshot, &entry) == TRUE) { while (Process32Next(snapshot, &entry) == TRUE) { if (wcscmp(entry.szExeFile, L"detours-example.exe") == 0) { InjectDll(entry.th32ProcessID, L"test-dll.dll"); std::cout << "Enter to exit"; std::cin.get(); EjectDll(entry.th32ProcessID, L"test-dll.dll"); break; } } }
我们看下detour注入的部分,也就是test-dll的代码:
// dllmain.cpp : DLL 애플리케이션의 진입점을 정의합니다. // #include "detours.h" #include "pch.h" #include "../Detours/include/detours.h" static int (WINAPI* NativeMessageBox)( HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType ) = MessageBox; int WINAPI MyMessageBox( HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType) { return NativeMessageBox(hWnd, L"Detours Hooking!", lpCaption, uType); } BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { if (DetourIsHelperProcess()) { return TRUE; } switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: DetourRestoreAfterWith(); DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourAttach(&(PVOID&)NativeMessageBox, MyMessageBox); DetourTransactionCommit(); break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourDetach(&(PVOID&)NativeMessageBox, MyMessageBox); DetourTransactionCommit(); break; } return TRUE; }
其核心是将NativeMessageBox 修改为自己的标题,看起来也没啥复杂的!
从这个例子看,使用detour进行注入还是比较干净整洁的!
todo:我自己注入一个ssl socket的试试。
标签:return,windows,dll,hook,api,detours,FALSE,NULL,TRUE From: https://www.cnblogs.com/bonelee/p/17011282.html