• 使用VC++通过远程进程注入来实现HOOK指定进程的某个API


    前阵子读到一篇关于《HOOK API入门之Hook自己程序的MessageBoxW》的博客,博客地址:http://blog.csdn.net/friendan/article/details/12222651,感觉写的很好但这篇博客主要讲的是本进程(本程序)的API HOOK那么如何将DLL注入到远程进程并进行API HOOK呢,好了废话不多说直接动手实践。

    创建DLL动态库(我是在vs2008上实现的)

    新建项目

    创建一个名为MyDLL(名字随便)win32项目(我创建的是win32  DLL)点击确定

    选择下一步

    选择DLL,并点击完成

    完成后到这个界面选择源文件中的dllmain.cpp如下图

    这样就已经创建好一个DLL了,创建好了应该在里面做点什么吧,那么下面我们就动手实践吧:)。

    我们这次HOOK的依然是MessageBoxW这个API。

    直接上代码:

      1 #include "stdafx.h"
      2 
      3 //原函数类型定义
      4 typedef int (WINAPI* MsgBoxW)(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType);
      5 MsgBoxW OldMsgBoxW = NULL;//指向原函数的指针
      6 FARPROC pfOldMsgBoxW;  //指向函数的远指针
      7 BYTE OldCode[5]; //原系统API入口代码
      8 BYTE NewCode[5]; //原系统API新的入口代码(jmp xxxxxxxx)
      9 
     10 HANDLE hProcess = NULL;//本程序进程句柄
     11 HINSTANCE hInst = NULL;//API所在的dll文件句柄
     12 
     13 void HookOn();
     14 void HookOff();
     15 int WINAPI MyMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType)
     16 {
     17     HookOff();//调用原函数之前,记得先恢复HOOK呀,不然是调用不到的
     18     //如果不恢复HOOK,就调用原函数,会造成死循环
     19     //毕竟调用的还是我们的函数,从而造成堆栈溢出,程序崩溃。
     20 
     21     int nRet = ::MessageBoxW(hWnd, L"哈哈,MessageBoxW被HOOK了", lpCaption, uType);
     22 
     23     HookOn();//调用完原函数后,记得继续开启HOOK,不然下次会HOOK不到。
     24 
     25     return nRet;
     26 }
     27 
     28 
     29 
     30 //开启钩子的函数
     31 void HookOn() 
     32 { 
     33     if ( NULL == hProcess)
     34     {
     35         return;
     36     }
     37 
     38     DWORD dwTemp=0;
     39     DWORD dwOldProtect;
     40 
     41     //修改API函数入口前个字节为jmp xxxxxx
     42     VirtualProtectEx(hProcess,pfOldMsgBoxW,5,PAGE_READWRITE,&dwOldProtect); 
     43     WriteProcessMemory(hProcess,pfOldMsgBoxW,NewCode,5,0);
     44     VirtualProtectEx(hProcess,pfOldMsgBoxW,5,dwOldProtect,&dwTemp);
     45 
     46 }
     47 
     48 //关闭钩子的函数
     49 void HookOff()
     50 { 
     51     if ( NULL == hProcess)
     52     {
     53         return;
     54     }
     55 
     56     DWORD dwTemp=0;
     57     DWORD dwOldProtect;
     58 
     59     //恢复API函数入口前个字节
     60     VirtualProtectEx(hProcess, pfOldMsgBoxW, 5, PAGE_READWRITE, &dwOldProtect); 
     61     WriteProcessMemory(hProcess, pfOldMsgBoxW, OldCode, 5, 0); 
     62     VirtualProtectEx(hProcess, pfOldMsgBoxW, 5, dwOldProtect, &dwTemp);  
     63 }
     64 
     65 //获取API函数入口前个字节
     66 //旧入口前个字节保存在前面定义的字节数组BYTE OldCode[5]
     67 //新入口前个字节保存在前面定义的字节数组BYTE NewCode[5]
     68 void GetApiEntrance()
     69 {
     70 
     71     //获取原API入口地址
     72     HMODULE hmod = ::LoadLibrary( L"User32.dll" );
     73     OldMsgBoxW = (MsgBoxW)::GetProcAddress(hmod, "MessageBoxW");
     74     pfOldMsgBoxW = (FARPROC)OldMsgBoxW;
     75 
     76     if (NULL == pfOldMsgBoxW)
     77     {
     78         MessageBox(NULL, L"获取原API入口地址出错", L"error!", 0);
     79         return;
     80     }
     81 
     82     // 将原API的入口前个字节代码保存到OldCode[]
     83     _asm 
     84     { 
     85         lea edi,OldCode        //获取OldCode数组的地址,放到edi
     86             mov esi,pfOldMsgBoxW //获取原API入口地址,放到esi
     87             cld      //方向标志位,为以下两条指令做准备
     88             movsd //复制原API入口前个字节到OldCode数组
     89             movsb //复制原API入口第个字节到OldCode数组
     90     }
     91 
     92 
     93     NewCode[0]=0xe9;//实际上xe9就相当于jmp指令
     94 
     95     //获取MyMessageBoxW的相对地址,为Jmp做准备
     96     //int nAddr= UserFunAddr –SysFunAddr - (我们定制的这条指令的大小);
     97     //Jmp nAddr;
     98     //(我们定制的这条指令的大小), 这里是,个字节嘛
     99     _asm 
    100     { 
    101         lea eax,MyMessageBoxW //获取我们的MyMessageBoxW函数地址
    102             mov ebx,pfOldMsgBoxW  //原系统API函数地址
    103             sub eax,ebx             //int nAddr= UserFunAddr –SysFunAddr
    104             sub eax,5             //nAddr=nAddr-5
    105             mov dword ptr [NewCode+1],eax //将算出的地址nAddr保存到NewCode后面个字节
    106             //注:一个函数地址占个字节
    107     } 
    108 
    109 
    110     //填充完毕,现在NewCode[]里的指令相当于Jmp MyMessageBoxW
    111     //既然已经获取到了Jmp MyMessageBoxW
    112     //现在该是将Jmp MyMessageBoxW写入原API入口前个字节的时候了
    113     //知道为什么是个字节吗?
    114     //Jmp指令相当于xe9,占一个字节的内存空间
    115     //MyMessageBoxW是一个地址,其实是一个整数,占个字节的内存空间
    116     //int n=0x123;   n占个字节和MyMessageBoxW占个字节是一样的
    117     //1+4=5,知道为什么是个字节了吧
    118     HookOn(); 
    119 }
    120 
    121 BOOL APIENTRY DllMain( HMODULE hModule,
    122                       DWORD  ul_reason_for_call,
    123                       LPVOID lpReserved
    124                       )
    125 {
    126     switch (ul_reason_for_call)
    127     {
    128     case DLL_PROCESS_ATTACH:
    129         {
    130             DWORD dwPid=::GetCurrentProcessId();
    131             hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,dwPid); 
    132             GetApiEntrance();
    133         }
    134         break;
    135     case DLL_THREAD_ATTACH:
    136         break;
    137     case DLL_THREAD_DETACH:
    138         break;
    139     case DLL_PROCESS_DETACH:
    140         HookOff();
    141             break;
    142     }
    143     return TRUE;
    144 }

    其中:

    DWORD dwPid=::GetCurrentProcessId();

    hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);

    这两行的作用是得到被注入DLL进程的进程句柄。

    好了DLL库部分已经解决,那让我看看远程注入DLL到指定进程部分的代码吧。

    在开始之前最好将DLL先编译出来,以便在下面的代码中使用。

    我创建的win32控制台应用程序来测试。

    直接上代码:)。

      1 // exe.cpp : 定义控制台应用程序的入口点。
      2 //
      3 
      4 #include "stdafx.h"
      5 #include <windows.h>
      6 #include <TlHelp32.h>
      7 #include <iostream>
      8 #include <time.h>
      9 
     10 BOOL InjectDllToRemoteProcess(const char* lpDllName, const char* lpPid, const char* lpProcName);
     11 
     12 int main(int argc, char* argv[])
     13 {
     14     PROCESS_INFORMATION pi;  
     15     STARTUPINFO si;  
     16     memset(&si,0,sizeof(si));  
     17     si.cb=sizeof(si);  
     18     si.wShowWindow=SW_SHOW;  
     19     si.dwFlags=STARTF_USESHOWWINDOW;  
     20     BOOL fRet=CreateProcess(_T("C:\Users\Administrator\Desktop\远程进程注入与HOOKapi例子\exe\ASD.exe"),NULL,NULL,FALSE ,NULL,NULL,NULL,NULL,&si,&pi);   
     21     //创建一个进程,这个进程可以是你自己写的MFC程序。
     22 
     23     if (!fRet)
     24     {
     25         //创建进程失败
     26         MessageBoxW(NULL,L"创建进程失败",L"error",MB_OK);
     27 
     28     }
     29 
     30     BOOL isInject = InjectDllToRemoteProcess("C:\Users\Administrator\Desktop\远程进程注入与HOOKapi例子\exe\MyDLL.dll", NULL , "ASD.exe");     
     31     //  C:\Users\Administrator\Desktop\远程进程注入与HOOKapi例子\exe\MyDLL.dll这个的DLL的路径
     32     //  ASD.exe是要注入的进程名,可以写一个MFC对话框程序在上面添加个按钮点击按钮弹出MessageBox看看你的MessageBox是不是被HOOK住了
     33 
     34     if (!isInject)
     35     {
     36         //注入远程进程失败
     37         MessageBoxW(NULL,L"注入远程进程失败",L"error",MB_OK);
     38     }
     39 
     40     while(1)
     41     {
     42 
     43     }
     44 
     45     return 0;
     46 }
     47 //进程快照(枚举各进程)
     48 BOOL GetPidByProcessName(LPCTSTR lpszProcessName , DWORD &dwPid)
     49 {
     50     HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
     51     if ( INVALID_HANDLE_VALUE == hSnapshot )
     52     {
     53         return FALSE;
     54     }
     55 
     56     PROCESSENTRY32 pe;
     57     pe.dwSize = sizeof(PROCESSENTRY32);
     58     if ( !Process32First(hSnapshot, &pe) )
     59     {
     60         ::CloseHandle(hSnapshot);
     61         return FALSE;
     62     }
     63 
     64     while ( Process32Next(hSnapshot, &pe) )
     65     {
     66         if ( !_stricmp(lpszProcessName, pe.szExeFile) )
     67         {
     68             ::CloseHandle(hSnapshot);
     69             dwPid = pe.th32ProcessID;
     70             return TRUE;
     71         }
     72     }
     73 
     74     ::CloseHandle(hSnapshot);
     75     return FALSE;
     76 }
     77 
     78 /********************************************************************************************************/
     79 
     80 //注入DLL到远程进程
     81 BOOL InjectDllToRemoteProcess(const char* lpDllName, const char* lpPid, const char* lpProcName)
     82 {
     83     DWORD dwPid = 0;
     84     if (NULL == lpPid || 0 == strlen(lpPid))
     85     {
     86         if (NULL != lpProcName && 0 != strlen(lpProcName))
     87         {
     88             if (!GetPidByProcessName(lpProcName, dwPid))
     89             {
     90                 return FALSE;
     91             }
     92         }
     93         else
     94         {
     95             return FALSE;
     96         }
     97     }
     98     else
     99     {
    100         dwPid = atoi(lpPid);
    101     }
    102 
    103 
    104     //根据Pid得到进程句柄(注意必须权限)
    105     HANDLE hRemoteProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, dwPid);
    106     if (INVALID_HANDLE_VALUE == hRemoteProcess)
    107     {
    108         return FALSE;
    109     }
    110 
    111     //计算DLL路径名需要的内存空间
    112     DWORD dwSize = (1 + lstrlenA(lpDllName)) * sizeof(char);
    113 
    114     //使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名缓冲区,成功返回分配内存的首地址.
    115     LPVOID lpRemoteBuff = (char *)VirtualAllocEx(hRemoteProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
    116     if (NULL == lpRemoteBuff)
    117     {
    118         CloseHandle(hRemoteProcess);
    119         return FALSE;
    120     }
    121 
    122     //使用WriteProcessMemory函数将DLL的路径名复制到远程进程的内存空间,成功返回TRUE.
    123     DWORD dwHasWrite = 0;
    124     BOOL bRet = WriteProcessMemory(hRemoteProcess, lpRemoteBuff, lpDllName, dwSize, &dwHasWrite);
    125     if (!bRet || dwHasWrite != dwSize)
    126     {
    127         VirtualFreeEx(hRemoteProcess, lpRemoteBuff, dwSize, MEM_COMMIT);
    128         CloseHandle(hRemoteProcess);
    129         return FALSE;
    130     }
    131 
    132     //创建一个在其它进程地址空间中运行的线程(也称:创建远程线程),成功返回新线程句柄.
    133     //注意:进程句柄必须具备PROCESS_CREATE_THREAD, PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE,和PROCESS_VM_READ访问权限
    134     DWORD  dwRemoteThread = 0;
    135     //LPTHREAD_START_ROUTINE pfnLoadLibrary = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("Kernel32"), "LoadLibraryA");
    136     //HANDLE hRemoteThread = CreateRemoteThread(hRemoteProcess, NULL, 0, pfnLoadLibrary, lpRemoteBuff, 0, &dwRemoteThread);
    137     HANDLE hRemoteThread = CreateRemoteThread(hRemoteProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryA, lpRemoteBuff, 0, &dwRemoteThread);
    138     if (INVALID_HANDLE_VALUE == hRemoteThread)
    139     {
    140         VirtualFreeEx(hRemoteProcess, lpRemoteBuff, dwSize, MEM_COMMIT);
    141         CloseHandle(hRemoteProcess);
    142         return FALSE;
    143     }
    144 
    145     //注入成功释放句柄
    146     WaitForSingleObject(hRemoteThread, INFINITE);
    147     CloseHandle(hRemoteThread);
    148     CloseHandle(hRemoteProcess);
    149 
    150 
    151     //补充:卸载过程(有bug)
    152     //准备卸载之前注入的Dll 
    153     //DWORD dwHandle, dwID;
    154     //LPVOID pFunc = GetModuleHandleA; //获得在远程线程中被注入的Dll的句柄 
    155     //HANDLE hThread = CreateRemoteThread(hRemoteProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, lpRemoteBuff, 0, &dwID);
    156     //WaitForSingleObject(hThread, INFINITE);
    157     //GetExitCodeThread(hThread, &dwHandle); //线程的结束码即为Dll模块儿的句柄 
    158     //CloseHandle(hThread);
    159     //pFunc = FreeLibrary;
    160     //hThread = CreateRemoteThread(hThread, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, (LPVOID)dwHandle, 0, &dwID); //将FreeLibraryA注入到远程线程中去卸载Dll 
    161     //WaitForSingleObject(hThread, INFINITE);
    162     //CloseHandle(hThread);
    163     //CloseHandle(hRemoteProcess);
    164 
    165     return TRUE;
    166 }

    代码的注释还是很清楚的,就不解释了。

  • 相关阅读:
    Java学习-068-多线程01:继承 Thread 类
    Linux-026-Centos Nginx 配置 pid 文件路径解决 service nginx status 提示:Can't open PID file /var/run/nginx.pid (yet?) after start: No such file or directory
    Linux-025-Centos Nginx 代理配置:同一端口代理不同服务
    Linux-024-Centos Nginx 代理配置:不同端口代理不同服务
    Linux-023-Centos Nginx Lua 脚本三种基本引用方式示例
    Linux-022-Centos Nginx 配置环境变量,常规命令
    Linux-021-Centos Nginx 配置服务管理,并设置开机启动
    Linux-020-Centos Shell 安装 Nginx 1.18.0
    Linux-019-Centos Shell 安装 Nginx 后启动时提示找不到Lua模块的libluajit-5.1.so.2文件,具体提示信息:./nginx: error while loading shared libraries: libluajit-5.1.so.2: cannot open shared object file: No such file or directory
    PySe-021-requests 使用 proxies 参数实现通过代理访问目标地址
  • 原文地址:https://www.cnblogs.com/ives-lu/p/4759660.html
Copyright © 2020-2023  润新知