<Win32 API> 钩子程序的实现

原文地址:Win32环境下代码注入与API钩子的实现

1. 主要点

挂API钩子分为四步:1. 查找并打开宿主进程,2. 将注入体装入宿主进程中运行,3. 用伪装函数替换目标API,4. 执行伪装函数。整个程序也分为两部分,一部分是负责查找并打开宿主进程和注入代码的应用程序,另一部分是包含修改代码和伪装函数的注入体。

1.1 获取宿主程序的ProcessId

亦可参考我的博文:通过进程名或进程ID获取进程句柄,窗口句柄


1.2 代码注入

步骤:

  1. 调用OpenProcess获取宿主进程句柄;
  2. 调用GetProcAddress查找LoadLibrary函数在宿主进程中的地址;
  3. 调用VirtualAllocEx和WriteProcessMemory将DLL文件名字符串写入宿主进程的内存;
  4. 调用CreateRemoteThread执行LoadLibrary在宿主进程中运行DLL;
  5. 调用VirtualFreeEx释放刚申请的内存;
  6. 调用WaitForSingleObject等待注入线程结束;
  7. 调用GetExitCodeThread获取前面加载的DLL的句柄;
  8. 调用CreateRemoveThead执行FreeLibrary卸载DLL;
  9. 调用CloseHandle关闭打开的所有句柄。

1.3 (dll端)替换目标API

修改入口点,实现JMP, 完美欺骗。

1.4 (dll端) 调用与恢复

恢复入口点,实现原有功能

主程序端代码:
#include <tchar.h>
#include <Windows.h>
#include <atlstr.h>
#include <Psapi.h>
#pragma comment(lib, "Psapi.lib")
 
#include <iostream>
#include <string>
using namespace std;
 
DWORD FindProc(LPCSTR lpName)
{
    DWORD aProcId[1024], dwProcCnt, dwModCnt;
    char szPath[MAX_PATH];
    HMODULE hMod;
 
    //枚举出所有进程ID
    if (!EnumProcesses(aProcId, sizeof(aProcId), &dwProcCnt))
    {
        //cout << "EnumProcesses error: " << GetLastError() << endl;
        return 0;
    }
 
    //遍例所有进程
    for (DWORD i = 0; i < dwProcCnt; ++i)
    {
        //打开进程,如果没有权限打开则跳过
        HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, aProcId[i]);
        if (NULL != hProc)
        {
            //打开进程的第1个Module,并检查其名称是否与目标相符
            if (EnumProcessModules(hProc, &hMod, sizeof(hMod), &dwModCnt))
            {
                GetModuleBaseNameA(hProc, hMod, szPath, MAX_PATH);
                if (0 == _stricmp(szPath, lpName))
                {
                    CloseHandle(hProc);
                    return aProcId[i];
                }
            }
            CloseHandle(hProc);
        }
    }
    return 0;
}
 
//第一个参数为宿主进程的映象名称,可以任务管理器中查看
//第二个参数为需要注入的DLL的完整文件名
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        cout << "Invalid parameters!" << endl;
        return -1;
    }
    //查找目标进程,并打开句柄
    DWORD dwProcID = FindProc(argv[1]);
    if (dwProcID == 0)
    {
        cout << "Target process not found!" << endl;
        return -1;
    }
    HANDLE hTarget = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcID);
    if (NULL == hTarget)
    {
        cout << "Can't Open target process!" << endl;
        return -1;
    }
 
    //获取LoadLibraryW和FreeLibrary在宿主进程中的入口点地址
    HMODULE hKernel32 = GetModuleHandle(_T("Kernel32"));
    LPTHREAD_START_ROUTINE pLoadLib = (LPTHREAD_START_ROUTINE)
        GetProcAddress(hKernel32, "LoadLibraryW");
    LPTHREAD_START_ROUTINE pFreeLib = (LPTHREAD_START_ROUTINE)
        GetProcAddress(hKernel32, "FreeLibrary");
    if (NULL == pLoadLib || NULL == pFreeLib)
    {
        cout << "Library procedure not found: " << GetLastError() << endl;
        CloseHandle(hTarget);
        return -1;
    }
 
    WCHAR szPath[MAX_PATH];
    MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, argv[2], -1,
        szPath, sizeof(szPath) / sizeof(szPath[0]));
 
    //在宿主进程中为LoadLibraryW的参数分配空间,并将参数值写入
    LPVOID lpMem = VirtualAllocEx(hTarget, NULL, sizeof(szPath),
        MEM_COMMIT, PAGE_READWRITE);
    if (NULL == lpMem)
    {
        cout << "Can't alloc memory block: " << GetLastError() << endl;
        CloseHandle(hTarget);
        return -1;
    }
 
    // 参数即为要注入的DLL的文件路径
    if (!WriteProcessMemory(hTarget, lpMem, (void*)szPath, sizeof(szPath), NULL))
    {
        cout << "Can't write parameter to memory: " << GetLastError() << endl;
        VirtualFreeEx(hTarget, lpMem, sizeof(szPath), MEM_RELEASE);
        CloseHandle(hTarget);
        return -1;
    }
 
    //创建信号量,DLL代码可以通过ReleaseSemaphore来通知主程序清理
    HANDLE hSema = CreateSemaphore(NULL, 0, 1, _T("Global\\InjHack"));
 
    //将DLL注入宿主进程
    HANDLE hThread = CreateRemoteThread(hTarget, NULL, 0, pLoadLib, lpMem, 0, NULL);
 
    //释放宿主进程内的参数内存
    VirtualFreeEx(hTarget, lpMem, sizeof(szPath), MEM_RELEASE);
 
    if (NULL == hThread)
    {
        cout << "Can't create remote thread: " << GetLastError() << endl;
        CloseHandle(hTarget);
        return -1;
    }
 
    //等待DLL信号量或宿主进程退出
    WaitForSingleObject(hThread, INFINITE);
    HANDLE hObj[2] = {hTarget, hSema};
    if (WAIT_OBJECT_0 == WaitForMultipleObjects(2, hObj, FALSE, INFINITE))
    {
        cout << "Target process exit." << endl;
        CloseHandle(hTarget);
        return 0;
    }
    CloseHandle(hSema);
 
    //根据线程退出代码获取DLL的Module ID
    DWORD dwLibMod;
    if (!GetExitCodeThread(hThread, &dwLibMod))
    {
        cout << "Can't get return code of LoadLibrary: " << GetLastError() << endl;
        CloseHandle(hThread);
        CloseHandle(hTarget);
        return -1;
    }
 
    //关闭线程句柄
    CloseHandle(hThread);
 
    //再次注入FreeLibrary代码以释放宿主进程加载的注入体DLL
    hThread = CreateRemoteThread(hTarget, NULL, 0, pFreeLib, (void*)dwLibMod, 0, NULL);
    if (NULL == hThread)
    {
        cout << "Can't call FreeLibrary: " << GetLastError() << endl;
        CloseHandle(hTarget);
        return -1;
    }
    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hThread);
 
    CloseHandle(hTarget);
    return 0;
}

DLL端代码:
#include <tchar.h>
#include <Windows.h>
 
//Handle of current process
HANDLE g_hProc;
 
//Backup of orignal code of target api
BYTE g_aBackup[6];
BYTE g_aOpcode[6];
 
//Critical section, prevent concurrency of calling the monitor
CRITICAL_SECTION g_cs;
 
//Base address of target API in DWORD
DWORD g_dwApiFunc = (DWORD)GetTickCount;
 
//Hook the target API
__inline BOOL MonitorBase(void)
{
    // Modify the heading 6 bytes opcode in target API to jmp instruction,
    // the jmp instruction will lead the EIP to our fake function
    return WriteProcessMemory(g_hProc, LPVOID(g_dwApiFunc),
        LPVOID(g_aOpcode), sizeof(g_aOpcode) / sizeof(g_aOpcode[0]), NULL);
}
 
//Unhook the target API
__inline BOOL ReleaseBase(void)
{
    // Restore the heading 6 bytes opcode of target API.
    return WriteProcessMemory(g_hProc, LPVOID(g_dwApiFunc),
        LPVOID(g_aBackup), sizeof(g_aOpcode) / sizeof(g_aOpcode[0]), NULL);
}
 
//Pre-declare
BOOL UninstallMonitor(void);
 
//Monitor Function
DWORD WINAPI MonFunc(DWORD dwErr)
{
    //Thread safety
    EnterCriticalSection(&g_cs);
 
    //Restore the original API before calling it
    ReleaseBase();
    DWORD dw = GetTickCount();
    MonitorBase();
 
    //You can do anything here, and you can call the UninstallMonitor
    //when you want to leave.
 
    //Thread safety
    LeaveCriticalSection(&g_cs);
    return dw;
}
 
//Install Monitor
BOOL InstallMonitor(void)
{
    //Get handle of current process
    g_hProc = GetCurrentProcess();
 
    g_aOpcode[0] = 0xE9; //JMP Procudure
    *(DWORD*)(&g_aOpcode[1]) = (DWORD)MonFunc - g_dwApiFunc - 5;

 ReadProcessMemory(g_hProc, LPVOID(g_dwApiFunc), LPVOID(g_aBackup), sizeof(g_aBackup)/ sizeof(g_aBackup[0]), NULL);
    InitializeCriticalSection(&g_cs);
 
    //Start monitor
    return MonitorBase();
}
 
BOOL UninstallMonitor(void)
{
    //Release monitor
    if (!ReleaseBase())
        return FALSE;
 
    DeleteCriticalSection(&g_cs);
 
    CloseHandle(g_hProc);
 
    //Synchronize to main application, release semaphore to free injector
    HANDLE hSema = OpenSemaphore(EVENT_ALL_ACCESS, FALSE, _T("Global\\InjHack"));
    if (hSema == NULL)
        return FALSE;
    return ReleaseSemaphore(hSema, 1, (LPLONG)g_hProc);
}
 
BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpvReserved)
{
    switch (fdwReason)
    {
    case DLL_PROCESS_ATTACH:
        DisableThreadLibraryCalls(hInstDll);
 
        InstallMonitor();
        break;
 
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

注:原文中未对g_aBackup进行初始化,需要在函数InstallMonitor中添加(所贴代码已添加):
ReadProcessMemory(g_hProc, LPVOID(g_dwApiFunc), LPVOID(g_aBackup), sizeof(g_aBackup)/ sizeof(g_aBackup[0]), NULL);



  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值