好久没有写博了,今天一下课就来写一篇,还是申明一下,菜鸟言论仅供娱乐,大牛看了可以忽略。
最近突发奇想,想试试R3下的线程注入,线程注入有好多中方法,我选得是钩子函数注入的方法,因为之前一直在做挂钩嘛,所以做起来比较容易,注入之后干嘛呢,我又纠结了很久,就把之前的APIHook的程序写进去吧,也正好想拿Windows的任务管理器开刀一下,挂钩TerminateProcess函数,成功了。我们直接切入正题。
钩子注入的原理其实很是简单,还是用SetWindowsHookEx函数安装钩子到指定的线程,使得线程加载我们所提供的动态链接库的函数,以达到让制定进程执行我们想要达到的目的,所以形象的称之为线程注入。
这里要注意的是,当SetWindowsHookEx的最后一个参数设置为其他线程的ID的时候,你的回调函数必须在一个dll文件中,因为进程相互间是独立的,只有你想要执行的目标函数在一个共享的dll文件中的时候才能被其他线程加载。这里想到一个问题,就是关于SetWindowsHookEx函数的第二个参数的问题,当时我怎样调试都发现SetWindowsHookEx不成功,GetLastError返回的是87,就是参数错误,所以我就很怀疑这个线程ID的参数,上网找了好久发现了一个名不经转的小论坛上有人说SetWindowsHookEx的第二个参数只能是自身进程的线程,而不能是其他进程的线程ID,于是我上CSDN上发帖,很多人也同意,直到“列宁”回帖反对,并在我们交流的时候,将网络版的MSDN关于这个函数的解释给我看了一下,才发现是可以的,找错了好久才发现,我对线程的ID取得有问题,我测试中发现连续用两个CreatToolhelp32Snapshot函数的时候无论你是否引用CloseHandle来结束CreatToolhelp32Snapshot函数的使用,都会使第二个CreatToolhelp32Snapshot函数得不到正确的结果,只是个人测试所得,未经验证。如果想得到一个制定进程的所有线程ID可以直接遍历系统所有线程然后从结构体的th32OwnerProcessID得到父进程ID进行比较即可。
另外还要说明的是,一般常见的方法的钩子注入,回调函数直接返回就行,要实现的目标代码一般放在dll文件的dllmain函数中,因为一个进程加载dll文件的时候会先执行dllmain函数,就可以达到我们的目的,但是本菜鸟不是这样做的,我的需要执行的目标代码直接放在毁掉函数中并在共享数据区设置了一个全局“哨兵”,使得我们的目标代码只执行一次,这种方法比较繁琐,不过也能实现想要实现的功能。
下面看看本菜鸟的代码片段,我是用MFC写的,所以不能把所有的都贴出来,核心代码弄出来了。
动态链接库代码(在上一篇博客的键盘钩子函数上稍作改动,就不把头文件,def文件都贴出来了,有问题看我的相关博客):
#include <windows.h> #define KEYHOOKLIB_EXPORTS #include "Hookdll.h" #include "stdio.h" BOOL boot=TRUE; HHOOK g_hHook=NULL; HMODULE WINAPI ModuleFromAddress(PVOID pv) { MEMORY_BASIC_INFORMATION mbi; if(::VirtualQuery(pv,&mbi,sizeof(mbi))!=0) return (HMODULE)mbi.AllocationBase; else return NULL; } BOOL WINAPI MyTerminateProcess(HANDLE hProcess,UINT uExitCode) { MessageBox(NULL,"操作违规!","ERROR",MB_OK); return 0; } LRESULT CALLBACK KeyHookProc(int nCode,WPARAM wParam,LPARAM IParam) { if(boot==TRUE)//检查是不是第一次执行,是第一次执行修改引入表 { HMODULE hMod=GetModuleHandle(NULL); DWORD dwOldprotect; MEMORY_BASIC_INFORMATION mbi; void * lpNewProc=MyTerminateProcess; IMAGE_DOS_HEADER * pDosHeader=(IMAGE_DOS_HEADER *)hMod; IMAGE_OPTIONAL_HEADER * pOptHeader=(IMAGE_OPTIONAL_HEADER *)((BYTE *)hMod+pDosHeader->e_lfanew+24); IMAGE_IMPORT_DESCRIPTOR * pImportDesc=(IMAGE_IMPORT_DESCRIPTOR *)((BYTE *)hMod+pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); while (pImportDesc->FirstThunk) { // printf("1success"); char * pszDllName=(char *)((BYTE *)hMod+pImportDesc->Name); if (stricmp(pszDllName,"kernel32.dll")==0) { break; } pImportDesc++; } if (pImportDesc->FirstThunk) { // printf("2success"); IMAGE_THUNK_DATA *pThunk=(IMAGE_THUNK_DATA *)((BYTE *)hMod+pImportDesc->FirstThunk); IMAGE_THUNK_DATA *oThunk=(IMAGE_THUNK_DATA *)((BYTE *)hMod+pImportDesc->OriginalFirstThunk); while (oThunk->u1.Function) { if(strcmp((char*)((ULONG)oThunk->u1.AddressOfData->Name+(ULONG)hMod),"TerminateProcess")==0) { WriteProcessMemory(GetCurrentProcess(),&pThunk->u1.Function,&lpNewProc,sizeof(void*),NULL); goto ok; } pThunk++; oThunk++; } } ok: boot=FALSE; } return CallNextHookEx(0,nCode,wParam,IParam); } BOOL WINAPI SetKeyHook(BOOL bInstall,DWORD dwThreadId) { BOOL bOk=FALSE; int k=0; int a[20]; if(bInstall) { g_hHook=::SetWindowsHookEx(WH_GETMESSAGE,KeyHookProc,ModuleFromAddress(KeyHookProc),dwThreadId); if(g_hHook==NULL) // a[k]=GetLastError(); // ::MessageBox(NULL,"g_hHook为NULL!","ERROR",MB_OK); bOk=(g_hHook!=NULL); } else { bOk=::UnhookWindowsHookEx(g_hHook); g_hHook=NULL; } return bOk; }
在主MFC程序中新建一个类,处理获得目标进程的所有线程ID,为类添加一个函数:
BOOL MyClass::MyHOOK(DWORD ProcessID) { THREADENTRY32 threadEntry={0}; BOOL flat=FALSE; threadEntry.dwSize=sizeof(threadEntry); HANDLE hThreadSnap=CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,0); if(hThreadSnap==INVALID_HANDLE_VALUE) { //cout<<"CreatToolhelp32Snapshot调试又失败!"<<endl; ::MessageBox(NULL,"CreatToolhelp32Snapshot调试失败!","ERROR",MB_OK); return FALSE; } BOOL bMore=Thread32First(hThreadSnap,&threadEntry); while(bMore) { if (threadEntry.th32OwnerProcessID==ProcessID) { flat=SetKeyHook(TRUE,threadEntry.th32ThreadID); if (flat==FALSE) { ::MessageBox(NULL,"SetKeyHook失败!","ERROR",MB_OK); return FALSE; } } bMore=Thread32Next(hThreadSnap, &threadEntry); } return TRUE; }
为主对话框添加消息响应函数:
void CInjexthookDlg::OnOK() { // TODO: Add extra validation here UpdateData(TRUE); if (m_PID==0) { ::MessageBox(NULL,"请输入要保护的进程PID!","错误",MB_OK); } else { //char *name=(LPSTR)(LPCTSTR)m_Name; if (MyClass::MyHOOK(m_PID)==TRUE) { ::MessageBox(NULL,"钩子安装成功!","祝贺",MB_OK); } else ::MessageBox(NULL,"钩子安装失败!","杯具",MB_OK); } // CDialog::OnOK(); }
测试的效果图如下(TerminateProcess.exe是被注入的程序,注入后不能正常关闭ss.exe):
另外要说的是,据我所知Taskmgr.exe由于版本不同,原理也不同,比如“列宁”的任务管理器就是使用GetProAddress来获得API函数的,而我的直接调用TerminateProcess,所以有时候Taskmgr.exe用IAT是挂不住的。
好了,本菜鸟大话说完了,还是那句话菜鸟言论仅供娱乐,大牛看了可以忽略。