AllocConsole() / FreeConsole()freopen("CONIN$", "r", stdin);
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
<conio.h>, <stdio.h>,即可使用 printf/scanf/system("pause")。JMP 函式體 指令(E9 xxxxxxxx),入口 = 函式名 + *(DWORD*)(函式名+1) + 入口樁指令長度。函式名 + 指令長度(不含退出指令)。系統 API 皆為此模式。於 API 函式出口處寫入兩條指令:
| 序 | 說明 | 指令 | 參數 |
|---|---|---|---|
| 1 | 呼叫掛鉤函式 | E8 xxxxxxxx | 相對偏移 = 掛鉤函式實際入口 - (API出口+5) |
| 2 | 返回並清理堆疊 | C2 xxxx | 參數總位元組數(如 SetLastError 為 4) |
使用 WriteProcessMemory(GetCurrentProcess(), API出口位址, ...) 寫入。
SetLastError 即時列印錯誤描述#include <stdio.h>
#include <windows.h>
void hook_SetLastError() {
if (GetLastError()) {
LPVOID lpMsgBuf = NULL;
if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), LANG_USER_DEFAULT,
(LPTSTR)&lpMsgBuf, 0, NULL)) {
printf("ERROR: %d %s", GetLastError(), (LPCSTR)lpMsgBuf);
LocalFree(lpMsgBuf);
}
}
}
int __stdcall WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
// 掛鉤指令模板:CALL hook + RET 4
unsigned char setup_SetLastError[8] = {0xE8, 0,0,0,0, 0xC2, 4, 0};
#ifdef _DEBUG
*(unsigned int*)(setup_SetLastError + 1) = (unsigned int)hook_SetLastError +
*(unsigned int*)((unsigned char*)hook_SetLastError + 1) - (unsigned int)SetLastError - 18;
#else
*(unsigned int*)(setup_SetLastError + 1) = (unsigned int)hook_SetLastError -
(unsigned int)SetLastError - 23;
#endif
// 安裝掛鉤
WriteProcessMemory(GetCurrentProcess(),
(LPVOID)((unsigned int)SetLastError + 18), setup_SetLastError, 8, new SIZE_T);
AllocConsole();
freopen("CONIN$", "r", stdin);
freopen("CONOUT$", "w", stdout);
// 此處放入自身業務代碼,所有 SetLastError 都會被攔截列印
// 移除掛鉤(恢復原 RET 4 指令)
WriteProcessMemory(GetCurrentProcess(),
(LPVOID)((unsigned int)SetLastError + 18), setup_SetLastError + 5, 3, new SIZE_T);
system("pause");
return 0;
}
hook_SetLastError 設計為無參數、無返回值,簡化堆疊操作。