一个API HOOK的例子
2010年07月20日
/* 编译环境 VC++6.0 WIN2K/WINXP,工程类型WIN32 APP,编译参数见最后的说明 */
#include
#include /*线程操作API*/
HINSTANCE hInst;/* 任务实例ID */
HWND MainFrmhWnd; /* 主窗口句柄 */
HFONT GlobalFont; /* 字体 */
typedef struct { /* 提供给注入代码的执行参数 */
DWORD MyID;/* 本任务ID */
void *FunPtr;/* 替换的API入口 */
} RemoteParam;
/* 窗口类名和标题的定义 */
#define MAINFRM_CLASS "MyHookApi"
#define MAINFRM_TITLE "Api Hook Demo"
ATOM MyRegisterClass(HINSTANCE hInstance);
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
MyRegisterClass(hInstance);
hInst = hInstance;
/* 创建主窗口 */
MainFrmhWnd = CreateWindow(MAINFRM_CLASS, MAINFRM_TITLE, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 200, 150, NULL, NULL, hInstance, NULL);
if (!MainFrmhWnd)
{
return FALSE;
}
/* 显示窗口 */
ShowWindow(MainFrmhWnd, nCmdShow);
UpdateWindow(MainFrmhWnd);
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, NULL, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}
/* 注册窗体类 */
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)NULL);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = MAINFRM_CLASS;
wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)NULL);
return RegisterClassEx(&wcex);
}
/* 通过任务名称获得任务ID */
DWORD GetTaskIdByName(char *TargetTaskName)
{
HANDLE hSnape;
PROCESSENTRY32 ProcInfo;
strupr(TargetTaskName);
hSnape = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
ProcInfo.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hSnape, &ProcInfo) == TRUE)
{
do
{
strupr(ProcInfo.szExeFile);
if (strcmp(ProcInfo.szExeFile, TargetTaskName) == 0)
{
CloseHandle(hSnape);
return ProcInfo.th32ProcessID;
}
} while(Process32Next(hSnape, &ProcInfo) == TRUE);
}
CloseHandle(hSnape);
return 0;
}
/* 创建传递给注入代码的参数 */
void * CreateDataStruct(HANDLE hProc, void *OldFunPtr)
{
RemoteParam remoteData;
RemoteParam* pRemoteParam;
ZeroMemory(&remoteData, sizeof(RemoteParam));
remoteData.MyID = GetCurrentProcessId();
remoteData.FunPtr = OldFunPtr;
/* 在目标任务里申请一块空间保存参数 */
pRemoteParam = (RemoteParam *)VirtualAllocEx(hProc, NULL, sizeof(RemoteParam), MEM_COMMIT, PAGE_READWRITE);
/* 写入参数 */
if (!WriteProcessMemory(hProc, pRemoteParam, &remoteData, sizeof(remoteData), 0))
{
MessageBox(MainFrmhWnd, "Write data to target process failed!", "Fail", MB_OK | MB_ICONERROR);
return 0;
}
return pRemoteParam;
}
/*提升进程访问权限*/
BOOL enableDebugPriv()
{
HANDLE hToken;
LUID sedebugnameValue;
TOKEN_PRIVILEGES tkp;
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
return FALSE;
}
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue)) {
CloseHandle(hToken);
return FALSE;
}
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = sedebugnameValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL)) {
CloseHandle(hToken);
return FALSE;
}
return TRUE;
}
/* 注入代码 */
HANDLE WINAPI MyOpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId)
{
HANDLE (*OldFunPtr)(DWORD, BOOL, DWORD);
RemoteParam * MyParam = (RemoteParam *)0xEFEFEFEF;/* 此处地址会被修改,设置成EFEFEFEF是为了便于查找*/
OldFunPtr = (HANDLE (*)(DWORD, BOOL, DWORD))MyParam->FunPtr;
/* 如果不等于当前任务ID,则允许打开(调用旧入口)*/
if (dwProcessId != MyParam->MyID)
{
return OldFunPtr(dwDesiredAccess, bInheritHandle, dwProcessId);
}
return 0;
}
/* 主函数,生成注入信息 */
void ProtectSelf(char *TargetTaskName)
{
DWORD TPid;
HANDLE hProc;
void * ParamPtr, * InjectPtr, * OldPtr, *ReplacePtr;
int JmpAddr, i;
unsigned char JmpCode[5] = {0};
unsigned char FunCode[4096] = {0};
/* 提升访问权限 */
enableDebugPriv();
/* 获得目标任务的PID */
TPid = GetTaskIdByName(TargetTaskName);
if (TPid == 0)
{
MessageBox(MainFrmhWnd, "Task not found", "Fail", MB_OK | MB_ICONERROR);
return;
}
/* 打开目标任务 */
hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, TPid);
if (hProc == 0)
{
MessageBox(MainFrmhWnd, "Open task failed", "Fail", MB_OK | MB_ICONERROR);
return;
}
/*
* 获得当前任务的要注入的API的地址,此地址是固定的,静态映射的,所以当前任务的地址
* 等于目标任务的地址。
*/
OldPtr = GetProcAddress(LoadLibrary("Kernel32"), "OpenProcess");
if (OldPtr == NULL)
{
MessageBox(MainFrmhWnd, "Get old function entry failed", "Fail", MB_OK | MB_ICONERROR);
CloseHandle(hProc);
return;
}
/*
* 申请两块地址,一个是保存注入代码,也就是自己的MyOpenProcess,另一块用于保存被覆盖的
* 系统的旧的OpenProcess的代码(实际只有5字节)
*/
InjectPtr = VirtualAllocEx(hProc, NULL, 4096, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
ReplacePtr = VirtualAllocEx(hProc, NULL, 4096, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (InjectPtr == NULL || ReplacePtr == NULL)
{
MessageBox(MainFrmhWnd, "Memory alloc failed!", "Fail", MB_OK | MB_ICONERROR);
CloseHandle(hProc);
return;
}
/* 创建并保存参数信息 */
ParamPtr = CreateDataStruct(hProc, ReplacePtr);
if (ParamPtr == NULL)
{
CloseHandle(hProc);
return;
}
memset(FunCode, 0, 4096);
/* 读出自身的这个函数到内存中 */
memcpy(FunCode, &MyOpenProcess, 4096);
for (i = 0; i 内存空间并写入代码完成的,自己的
替换函数如果判断此次调用的参数中,进程ID不是要屏蔽的进程号,则调用
系统旧的API,因为旧的API的前5个字节被替换成了LONG JMP,于是要把被
替换下来的内容保存于一个新的内存块里,并把这块内存的最后再加上一个
LONG JMP,跳转到实际的API的第5字节偏移处。
因为系统的调用都是采用stdcall方式,所以本程序需要编译成stdcall的
方式,否则会导致调用栈混乱,同时因为注入代码无法保存任何本任务内的
全局变量,所以需要专门开辟一段内存空间保存这些参数信息。注入代码内
不可以包含任何库、API函数调用,不能使用任何全局变量。
为了运行正常的需要,建议把编译参数里的/GZ去掉,否则此参数会产生
栈检查,可能会导致一些错误的地址出现。
*/
2010年07月20日
/* 编译环境 VC++6.0 WIN2K/WINXP,工程类型WIN32 APP,编译参数见最后的说明 */
#include
#include /*线程操作API*/
HINSTANCE hInst;/* 任务实例ID */
HWND MainFrmhWnd; /* 主窗口句柄 */
HFONT GlobalFont; /* 字体 */
typedef struct { /* 提供给注入代码的执行参数 */
DWORD MyID;/* 本任务ID */
void *FunPtr;/* 替换的API入口 */
} RemoteParam;
/* 窗口类名和标题的定义 */
#define MAINFRM_CLASS "MyHookApi"
#define MAINFRM_TITLE "Api Hook Demo"
ATOM MyRegisterClass(HINSTANCE hInstance);
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
MyRegisterClass(hInstance);
hInst = hInstance;
/* 创建主窗口 */
MainFrmhWnd = CreateWindow(MAINFRM_CLASS, MAINFRM_TITLE, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 200, 150, NULL, NULL, hInstance, NULL);
if (!MainFrmhWnd)
{
return FALSE;
}
/* 显示窗口 */
ShowWindow(MainFrmhWnd, nCmdShow);
UpdateWindow(MainFrmhWnd);
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, NULL, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}
/* 注册窗体类 */
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)NULL);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = MAINFRM_CLASS;
wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)NULL);
return RegisterClassEx(&wcex);
}
/* 通过任务名称获得任务ID */
DWORD GetTaskIdByName(char *TargetTaskName)
{
HANDLE hSnape;
PROCESSENTRY32 ProcInfo;
strupr(TargetTaskName);
hSnape = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
ProcInfo.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hSnape, &ProcInfo) == TRUE)
{
do
{
strupr(ProcInfo.szExeFile);
if (strcmp(ProcInfo.szExeFile, TargetTaskName) == 0)
{
CloseHandle(hSnape);
return ProcInfo.th32ProcessID;
}
} while(Process32Next(hSnape, &ProcInfo) == TRUE);
}
CloseHandle(hSnape);
return 0;
}
/* 创建传递给注入代码的参数 */
void * CreateDataStruct(HANDLE hProc, void *OldFunPtr)
{
RemoteParam remoteData;
RemoteParam* pRemoteParam;
ZeroMemory(&remoteData, sizeof(RemoteParam));
remoteData.MyID = GetCurrentProcessId();
remoteData.FunPtr = OldFunPtr;
/* 在目标任务里申请一块空间保存参数 */
pRemoteParam = (RemoteParam *)VirtualAllocEx(hProc, NULL, sizeof(RemoteParam), MEM_COMMIT, PAGE_READWRITE);
/* 写入参数 */
if (!WriteProcessMemory(hProc, pRemoteParam, &remoteData, sizeof(remoteData), 0))
{
MessageBox(MainFrmhWnd, "Write data to target process failed!", "Fail", MB_OK | MB_ICONERROR);
return 0;
}
return pRemoteParam;
}
/*提升进程访问权限*/
BOOL enableDebugPriv()
{
HANDLE hToken;
LUID sedebugnameValue;
TOKEN_PRIVILEGES tkp;
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
return FALSE;
}
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue)) {
CloseHandle(hToken);
return FALSE;
}
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = sedebugnameValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL)) {
CloseHandle(hToken);
return FALSE;
}
return TRUE;
}
/* 注入代码 */
HANDLE WINAPI MyOpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId)
{
HANDLE (*OldFunPtr)(DWORD, BOOL, DWORD);
RemoteParam * MyParam = (RemoteParam *)0xEFEFEFEF;/* 此处地址会被修改,设置成EFEFEFEF是为了便于查找*/
OldFunPtr = (HANDLE (*)(DWORD, BOOL, DWORD))MyParam->FunPtr;
/* 如果不等于当前任务ID,则允许打开(调用旧入口)*/
if (dwProcessId != MyParam->MyID)
{
return OldFunPtr(dwDesiredAccess, bInheritHandle, dwProcessId);
}
return 0;
}
/* 主函数,生成注入信息 */
void ProtectSelf(char *TargetTaskName)
{
DWORD TPid;
HANDLE hProc;
void * ParamPtr, * InjectPtr, * OldPtr, *ReplacePtr;
int JmpAddr, i;
unsigned char JmpCode[5] = {0};
unsigned char FunCode[4096] = {0};
/* 提升访问权限 */
enableDebugPriv();
/* 获得目标任务的PID */
TPid = GetTaskIdByName(TargetTaskName);
if (TPid == 0)
{
MessageBox(MainFrmhWnd, "Task not found", "Fail", MB_OK | MB_ICONERROR);
return;
}
/* 打开目标任务 */
hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, TPid);
if (hProc == 0)
{
MessageBox(MainFrmhWnd, "Open task failed", "Fail", MB_OK | MB_ICONERROR);
return;
}
/*
* 获得当前任务的要注入的API的地址,此地址是固定的,静态映射的,所以当前任务的地址
* 等于目标任务的地址。
*/
OldPtr = GetProcAddress(LoadLibrary("Kernel32"), "OpenProcess");
if (OldPtr == NULL)
{
MessageBox(MainFrmhWnd, "Get old function entry failed", "Fail", MB_OK | MB_ICONERROR);
CloseHandle(hProc);
return;
}
/*
* 申请两块地址,一个是保存注入代码,也就是自己的MyOpenProcess,另一块用于保存被覆盖的
* 系统的旧的OpenProcess的代码(实际只有5字节)
*/
InjectPtr = VirtualAllocEx(hProc, NULL, 4096, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
ReplacePtr = VirtualAllocEx(hProc, NULL, 4096, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (InjectPtr == NULL || ReplacePtr == NULL)
{
MessageBox(MainFrmhWnd, "Memory alloc failed!", "Fail", MB_OK | MB_ICONERROR);
CloseHandle(hProc);
return;
}
/* 创建并保存参数信息 */
ParamPtr = CreateDataStruct(hProc, ReplacePtr);
if (ParamPtr == NULL)
{
CloseHandle(hProc);
return;
}
memset(FunCode, 0, 4096);
/* 读出自身的这个函数到内存中 */
memcpy(FunCode, &MyOpenProcess, 4096);
for (i = 0; i 内存空间并写入代码完成的,自己的
替换函数如果判断此次调用的参数中,进程ID不是要屏蔽的进程号,则调用
系统旧的API,因为旧的API的前5个字节被替换成了LONG JMP,于是要把被
替换下来的内容保存于一个新的内存块里,并把这块内存的最后再加上一个
LONG JMP,跳转到实际的API的第5字节偏移处。
因为系统的调用都是采用stdcall方式,所以本程序需要编译成stdcall的
方式,否则会导致调用栈混乱,同时因为注入代码无法保存任何本任务内的
全局变量,所以需要专门开辟一段内存空间保存这些参数信息。注入代码内
不可以包含任何库、API函数调用,不能使用任何全局变量。
为了运行正常的需要,建议把编译参数里的/GZ去掉,否则此参数会产生
栈检查,可能会导致一些错误的地址出现。
*/