日期:2014-05-17  浏览次数:20888 次

Windows下如何改写目标进程的窗口函数来注入DLL

Windows下如何改写目标进程的窗口函数来注入DLL
2011年02月21日
  Windows的UI线程简单地说就是创建了窗口的线程,
  其创建的窗口都有窗口函数,在这里,我介绍一个改写
  UI线程窗口过程来注入DLL的方法,不需要调用VirtualAllocEx,也不需要GetThreadContext和SetThreadContext
  对付一些常用的杀毒软件应该没有问题
  一:获取一个进程的窗口句柄,例如Notepad.exe进程的主窗口类名是 "NOTEPAD"
  通过 FindWindowW(L"NOTEPAD",NULL) 就可以得到运行的 Notepad.exe 的
  主窗口的句柄设为 hWndNotepad, 通过 GetWindowThreadProcessId()
  就可以获取到 创建该窗口的线程ID和Notepad.exe进程ID,通过 GetClassLongW(
  hWndNotepad,GCL_WNDPROC)可以得到窗口过程函数地址。
  代码大致如下:
  HWND hWndNotepad = ::FindWindowW(L"NOTEPAD",NULL);
  DWORD proID;
  DWORD dThreadID = ::GetWindowThreadProcessId(hWndNotepad ,&proID);
  HANDLE hPRocess = ::OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE ,FALSE,proID);
  HANDLE hThread = ::OpenThread(THREAD_SUSPEND_RESUME,dThreadID);
  //注意必须用UNICODE,否则取出来的不是窗口函数地址,而是一个HANDLE值
  DWORD dWndProc = ::GetClassLongW(hWndNotepad,GCL_WNDPROC); 
  //更改窗口函数地址所在内存页面的控制标志,使之能够被覆盖
  DWORD dpOld;
  ::VirtualProtectEx(hPRocess,LPVOID(dWndProc),8192, PAGE_EXECUTE_READWRITE,&dpOld)
  二:创建注入的函数
  typedef HMODULE (__stdcall *pLoadLibrary)(LPCSTR lpLibFileName);
  //必须定义如下4个参数,否则目标线程会发生函数调用错误
  LRESULT __stdcall test(HWND hwnd,UINT uMsg,WPARAM wPAram,LPARAM lParam)
  {
  //不能直接使用常量字符串,否则会引起目标进程读取数据异常。
  char dllname[] = {'c',':','\\','d','l','l','t','e','s','t','.','d', 'l','l','\0'};
  //必须用2088770939这个 LoadLibrary 函数的绝对地址(在我的xp下是这个地址,使用时应该在自己的windows下获取)
  pLoadLibrary pFunc = pLoadLibrary(2088770939);
  //调用LoadLibrary,因为LoadLibrary函数在windows下每个进程中绝对地址都是一样的。
  pFunc(dllname);
  return;
  }
  然后将目标线程挂起
  ::SuspendThread(hThread );
  接着将目标窗口过程一部分或全部读出来
  BYTE sCodes[1024];
  DWORD dLen;
  ::ReadProcessMemory(hPRocess,(LPVOID)dWndProc,sCod es,1024, &dLen);
  将目标窗口过程一部分或全部用我们的函数覆盖
  ::WriteProcessMemory(hProcess,pRemote,(LPVOID)test ,1024,&dWriten);
  此时目标窗口函数已经被我们改写了,如果唤醒目标线程的话,单击TASKBAR上的记事本窗口
  我们的dll就会被自动装载了,但此时目标线程的窗口过程已经无法恢复了,我们需要做恢复工作
  恢复工作思路是 在装载的dll里DllMain函数编写代码获取到之前
  备份的目标窗口函数的1024字节内容,将我们覆盖的1024字节恢复,同时调用
  ::SetClassLongW()替换窗口过程函数,让窗口过程恢复之前的工作。
  如何获取呢?.....对了,使用共享内存,主控程序建立有名称的共享内存(FileMapping),将上述
  数据写入共享内存中,恢复的时候就从共享内存中读取数据,代码如下:
  HANDLE hfileMap = ::CreateFileMappingW(INVALID_HANDLE_VALUE,NULL,PAG E_READWRITE,\
  0,4096,L"注入DLL的测试");
  LPVOID lpAddr = ::MapViewOfFile(hfileMap,FILE_MAP_WRITE,0,0,4096)/ /映射整个4096字节范围。
  //将窗口句柄写入
  BYTE *pStart = (BYTE*)lpAddr;
  memcpy(pStart,&hWndNotepad,sizeof(HWND));
  pStart += sizeof(HWND);
  //将备份的目标窗口函数的1024字节内容写入
  memcpy(pStart,sCodes,1024);
  //等待第三步创建好dll后,唤醒目标线程
  ::ResumeThread(hThread);
  //发送windows消息使得目标线程执行覆盖的窗口函数
  ::SendMessage(hWndNotepad,99999999,0,0)
  //注意不能立即结束控制进程,必须等待到dll装载后才能结束控制进程,否则
  //之前创建的共享内存将不能够被映射,这里仅仅是调用Sleep
  ::Sleep(9999999);
  三:编写我们的dll
  #include 
  WNDPROC g_WndPRoc = 0;
  LRESULT CALLBACK newWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
  {
  return ::CallWindowProcW(g_WndPRoc,hWnd,uMsg,wParam,lPara m);     }      BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
  {             if(DLL_PROCESS_ATTACH == ul_reason_for_call)             {                    //不能调用messagebox等UI函数,这样会导致窗口过程函数重入,导致崩溃
  //写log到文件中便于观察
  HANDLE hFile = ::CreateFile("C:\\dllTest.log",GENERIC_READ|GENERI C_WRITE, \
  FILE_SHARE_READ,NULL,OPEN_ALWAYS, \