- 注册时间
- 2021-4-16
- 最后登录
- 2024-3-30
- 在线时间
- 4 小时
编程入门
- 龙马币
- 88
|
windows默认会用异常处理程序包含SetTimer的回调函数TimerProc,所以可以利用这一点给远程注入代码加上异常处理过程,防止注入代码导致远程程序异常崩溃。并且异常处理过程由windows自动处理,不需要编写额外代码,非常方便。
微软文档所述:
Windows 会将其对 TimerProc 的 调用括起来,其中包含使用和放弃所有异常的异常处理程序。 这是自 Windows 2000 以来的默认行为,但在 Windows 的未来版本中,这一行为可能会更改。 https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-setuserobjectinformationw
验证SetTimer函数自带异常处理
首先必须知道的是由windows消息队列来调用TimerProc函数。具体的说是SetTimer函数带回调函数时,由DispatchMessage函数调用TimerProc函数,且触发异常后,异常以后的代码不会被执行。
新建VS工程,选windows控制台程序,完整代码如下。
- #include <windows.h>
- #include <iostream>
- using namespace std;
- // 计时器回调函数
- VOID CALLBACK Timerproc(
- HWND hwnd, // handle to window for timer messages
- UINT message, // WM_TIMER message
- UINT idTimer, // timer identifier
- DWORD dwTime) // current system time
- {
- cout << "Timerproc" << endl;
- int a = 1, b = 0, c = 0;
- c = a / b; // 这句代码会引发异常,除数为零
- cout << c << endl;
- }
- int main() {
- //const HWND hwnd = GetConsoleWindow(); // 获取本进程句柄
- const UINT_PTR timer_id = SetTimer(nullptr, // 绑定的窗口句柄
- 0, // timer ID
- 0x0, // 倒计时数,毫秒
- Timerproc); // 回调函数
- int Counter = 0;
- MSG msg;
- while (GetMessage(&msg, nullptr, 0, 0)) {
- ++Counter;
- cout << "Counter: " << Counter << "; message: " << msg.message << '\n';
- DispatchMessage(&msg);
- }
- return 0;
- }
复制代码
调试运行,VS输出标签里重复提示除数为零异常,但程序仍然正常运行,没有显示cout << c << endl;这句代码,说明异常处理程序确实包含了TimerProc函数。
新线程内执行TimerProc函数
控制台程序
远程注入的代码是通过CreateRemoteThread函数执行,也就是新建一个线程。所以先来模拟本地新线程执行情景。
- #include <windows.h>
- #include <iostream>
- #include <process.h>
- using namespace std;
- HANDLE hEvent;
- // 计时器回调函数
- VOID CALLBACK Timerproc(
- HWND hwnd, // handle to window for timer messages
- UINT message, // WM_TIMER message
- UINT idTimer, // timer identifier
- DWORD dwTime) // current system time
- {
- cout << "Timerproc" << endl;
- KillTimer(hwnd, idTimer);
- int a = 1, b = 0, c = 0;
- c = a / b; // 这句代码会引发异常,除数为零
- cout << c << endl;
- SetEvent(hEvent);
- }
- DWORD WINAPI thread(_In_ LPVOID lpParameter) {
- HWND process_hwnd = *(HWND*)lpParameter;
- cout << "process_hwnd=" << process_hwnd << endl;
- cout << "thread start PID=" << GetCurrentThreadId() << endl;
- UINT_PTR timer_id;
- const HANDLE thread_hwnd = GetCurrentThread();
- timer_id = SetTimer(nullptr, 0, 0, nullptr);
- timer_id = SetTimer(process_hwnd, // 绑定的窗口句柄
- timer_id, // timer ID
- 0x0, // 倒计时数,毫秒
- Timerproc); // 回调函数
- hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
- WaitForSingleObject(hEvent, 5000);
- cout << "WaitForSingleObject end" << endl;
- CloseHandle(hEvent);
- return 0;
- }
- int main() {
- //HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
- //cout << "process_handle=" << process_handle << endl;
- HWND process_hwnd = GetConsoleWindow();
- cout << "process_hwnd=" << process_hwnd << endl;
- const HANDLE thread_handle = CreateThread(nullptr, 0, thread, &process_hwnd, 0, nullptr);
- cout << "CreateThread thread_handle=" << thread_handle << endl;
- BOOL bRet;
- int Counter = 0;
- MSG msg;
- while ((bRet = GetMessage(&msg, nullptr, 0, 0)) != 0) {
- if (bRet == -1) {
- // handle the error and possibly exit
- }
- else {
- ++Counter;
- cout << "Counter: " << Counter << "; message: " << msg.message << '\n';
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
- return 0;
- }
复制代码
上面的代码无法运行成功,原因是TimeProc函数并未执行。没有执行是因为消息队列没有消息,那么GetMessage就会阻塞进程,不执行while循环体。不进入循环体,DispatchMessage函数就无法调用TimeProc函数。
至于为什么这里SetTimer函数没有触发消息……我也不知道。
窗口程序
控制台程序无法成功运行,但是换窗口程序却可以成功运行。
代码流程:
WinMain函数是主函数,主函数中创建新线程执行thread函数。
thread函数中创建事件,新建空计时器。新建空计时器是为了避免覆盖注入进程中已有的计时器(当SetTimer函数的第1、2参数为0时,windows会自动在当前进程创建新的计时器ID)。然后再次设置计时器,传入注入进程句柄、新建空计时器ID、Timerproc回调函数。最后执行WaitForSingleObject函数阻塞等待事件信号,如果Timerproc函数触发异常了,则会超时指定时间后继续执行。
Timerproc函数可以自动处理异常,需要注意的是触发异常后的代码不会被执行。函数首先关闭计时器,接着直接插入注入代码或调用call函数,最后设置信号为有状态,以便让WaitForSingleObject函数接收信号。
call函数有没有都可以,单独的call函数方便编程和调试。
改进的代码如下:
- #include <iostream>
- #include <windows.h>
- using namespace std;
- static const TCHAR gc_szClassName[] = TEXT("Test");
- static const TCHAR gc_szWindowTitle[] = TEXT("Test");
- HANDLE hEvent;
- void call() {
- int a = 1, b = 0, c = 0;
- c = a / b; // 这句代码会引发异常,除数为零
- cout << c << endl;
- }
- // 计时器回调函数
- VOID CALLBACK Timerproc(
- HWND hwnd, // handle to window for timer messages
- UINT message, // WM_TIMER message
- UINT idTimer, // timer identifier
- DWORD dwTime) // current system time
- {
- cout << "Timerproc" << endl;
- if (KillTimer(hwnd, idTimer))
- cout << "KillTimer done" << endl;
- else
- cout << "KillTimer error" << endl;
- call(); // 如果函数内异常导致退出,则不会执行下面的代码
- SetEvent(hEvent);
- }
- DWORD WINAPI thread(_In_ LPVOID lpParameter) {
- HWND process_hwnd = *(HWND*)lpParameter;
- cout << "process_hwnd=" << process_hwnd << endl;
- cout << "thread start PID=" << GetCurrentThreadId() << endl;
- UINT_PTR timer_id;
- const HANDLE thread_hwnd = GetCurrentThread();
- hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
- timer_id = SetTimer(nullptr, 0, 0, nullptr);
- timer_id = SetTimer(process_hwnd, // 绑定的窗口句柄
- timer_id, // timer ID
- 0x0, // 倒计时数,毫秒
- Timerproc); // 回调函数
- cout << "timer_id=" << timer_id << endl;
- WaitForSingleObject(hEvent, 5000);
- cout << "WaitForSingleObject end" << endl;
- CloseHandle(hEvent);
- return 0;
- }
- LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam) {
- switch (uMessage) {
- case WM_CREATE:
- break;
- case WM_CLOSE:
- DestroyWindow(hWnd);
- break;
- case WM_DESTROY:
- PostQuitMessage(EXIT_SUCCESS);
- break;
- default:;
- }
- return DefWindowProc(hWnd, uMessage, wParam, lParam);
- }
- int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCommandLine, int nShowCommand) {
- // define variables
- HWND hWnd;
- WNDCLASS wc;
- MSG msg;
- // unused parameters
- UNREFERENCED_PARAMETER(hPrevInstance);
- UNREFERENCED_PARAMETER(lpszCommandLine);
- UNREFERENCED_PARAMETER(nShowCommand);
- // initialize WNDCLASS structure
- ZeroMemory(&wc, sizeof(wc));
- wc.lpfnWndProc = WndProc;
- wc.hInstance = hInstance;
- wc.lpszClassName = gc_szClassName;
- // attempt to register the class
- if (RegisterClass(&wc) != 0) {
- // show console
- AllocConsole();
- std::ignore = freopen_s((FILE**)stdout, "CONOUT$", "w", stdout);
- std::ignore = freopen_s((FILE**)stdout, "CONOUT$", "w", stderr);
- std::ignore = freopen_s((FILE**)stdout, "CONIN$", "r", stdin);
- std::cout.clear();
- std::clog.clear();
- std::cerr.clear();
- std::cin.clear();
- // attempt to create the window
- hWnd = CreateWindow(gc_szClassName, gc_szWindowTitle, WS_CAPTION | WS_SYSMENU | WS_VISIBLE, 100, 100, 100, 100, NULL, NULL, hInstance, NULL);
- cout << "window hwnd=" << hWnd << endl;
- const HANDLE thread_handle = CreateThread(nullptr, 0, thread, &hWnd, 0, nullptr);
- int Counter = 0;
- // retrieve messages
- while (GetMessage(&msg, NULL, 0, 0) > 0) {
- ++Counter;
- std::cout << "Counter: " << Counter << "; message: " << msg.message << '\n';
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- // use the return-code from the window
- return (int)msg.wParam;
- }
- return EXIT_FAILURE;
- }
复制代码
将注入函数转为汇编语言
转为汇编语言这一步骤我卡住好几天。转为汇编语言本进程执行很简单,但如果要把thread()、timerproc()、call()三个函数在远程进程运行则有难度。主要难度为注入到远程进程后,由于没有基址重定位,无法执行call系统函数语句CALL KillTimer,也无法执行call自有函数语句CALL call。
研究了几天之后发现受限于内联汇编的功能局限性,不能用内联汇编完成目的。所以简单学习了一下asmjit,改用asmjit汇编引擎实现功能。
本进程执行代码
接下来需要将thread()、Timerproc()、call()函数转换为汇编语言。转换后的完整代码如下:
- #include <iostream>
- #include <windows.h>
- using namespace std;
- HANDLE hEvent;
- void call() {
- //cout << "func call start" << endl;
- //int a = 1, b = 0, c = 0;
- //c = a / b; // 这句代码会引发异常,除数为零
- //cout << "func call end" << endl;
- char start[] = "func call start\n";
- char end[] = "func call end\n";
- __asm {
- lea eax, start
- push eax
- call printf
- xor eax, eax
- div eax // 0除以0 异常语句
- lea eax, end
- push eax
- call printf
- }
- }
- // 计时器回调函数
- VOID CALLBACK Timerproc(
- HWND hwnd, // handle to window for timer messages
- UINT message, // WM_TIMER message
- UINT idTimer, // timer identifier
- DWORD dwTime) // current system time
- {
- //cout << "Timerproc" << endl;
- //if (KillTimer(hwnd, idTimer))
- // cout << "KillTimer done" << endl;
- //else
- // cout << "KillTimer error" << endl;
- //call(); // 如果函数内异常导致退出,则不会执行下面的代码
- //SetEvent(hEvent);
- __asm {
- push idTimer
- push hwnd
- call KillTimer
- call call
- //mov dword ptr ds : [0x12345678] , eax; // 把call函数的返回值结果存放到指定的内存地址
- push hEvent
- call SetEvent
- }
- }
- DWORD WINAPI thread(_In_ LPVOID lpParameter) {
- HWND process_hwnd = *(HWND*)lpParameter;
- UINT_PTR timer_id;
- //cout << "process_hwnd=" << process_hwnd << endl;
- //cout << "thread start PID=" << GetCurrentThreadId() << endl;
- //const HANDLE thread_hwnd = GetCurrentThread();
- ////hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
- //timer_id = SetTimer(nullptr, 0, 0, nullptr);
- //timer_id = SetTimer(process_hwnd, // 绑定的窗口句柄
- // timer_id, // timer ID
- // 0x0, // 倒计时数,毫秒
- // Timerproc); // 回调函数
- //cout << "timer_id=" << timer_id << endl;
- //WaitForSingleObject(hEvent, 5000);
- //cout << "WaitForSingleObject end" << endl;
- //CloseHandle(hEvent);
- char start[] = "func thread start\n";
- char end[] = "func thread end\n";
- __asm {
- lea eax, start
- push eax
- call printf
- push 0
- push 0
- push 0
- push 0
- call CreateEvent
- mov hEvent, eax
- push 0
- push 0
- push 0
- push 0
- call SetTimer
- push Timerproc
- push 0
- push eax
- push process_hwnd
- call SetTimer
- mov timer_id, eax
- push 0x1388; 5000毫秒
- push hEvent
- call WaitForSingleObject
- push hEvent
- call CloseHandle
- lea eax, end
- push eax
- call printf
- }
- return 0;
- }
- LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam) {
- switch (uMessage) {
- case WM_CREATE:
- break;
- case WM_CLOSE:
- DestroyWindow(hWnd);
- break;
- case WM_DESTROY:
- PostQuitMessage(EXIT_SUCCESS);
- break;
- default:;
- }
- return DefWindowProc(hWnd, uMessage, wParam, lParam);
- }
- int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCommandLine, int nShowCommand) {
- // define variables
- HWND hWnd;
- WNDCLASS wc;
- MSG msg;
- // unused parameters
- UNREFERENCED_PARAMETER(hPrevInstance);
- UNREFERENCED_PARAMETER(lpszCommandLine);
- UNREFERENCED_PARAMETER(nShowCommand);
- // initialize WNDCLASS structure
- ZeroMemory(&wc, sizeof(wc));
- wc.lpfnWndProc = WndProc;
- wc.hInstance = hInstance;
- wc.lpszClassName = TEXT("class name");
- // attempt to register the class
- if (RegisterClass(&wc) != 0) {
- AllocConsole();
- std::ignore = freopen_s((FILE**)stdout, "CONOUT$", "w", stdout);
- std::ignore = freopen_s((FILE**)stdout, "CONOUT$", "w", stderr);
- std::ignore = freopen_s((FILE**)stdout, "CONIN$", "r", stdin);
- std::cout.clear();
- std::clog.clear();
- std::cerr.clear();
- std::cin.clear();
- // attempt to create the window
- hWnd = CreateWindow(TEXT("class name"), TEXT("window title"), WS_CAPTION | WS_SYSMENU | WS_VISIBLE, 1100, 600, 100, 100, NULL, NULL, hInstance, NULL);
- cout << "window hwnd=" << hWnd << endl;
- const HANDLE thread_handle = CreateThread(nullptr, 0, thread, &hWnd, 0, nullptr);
- int Counter = 0;
- // retrieve messages
- while (GetMessage(&msg, NULL, 0, 0) > 0) {
- ++Counter;
- std::cout << "Counter: " << Counter << "; message: " << msg.message << '\n';
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- // use the return-code from the window
- return (int)msg.wParam;
- }
- return EXIT_FAILURE;
- }
复制代码
运行成功。实际应用时可以把call函数里的两个调试printf语句删了。
远程进程执行代码
代码只适用于x86程序,如果你系统是64位系统,测试用的笔记本程序需要使用c:\windows\SysWOW64\notepad.exe
- #include <algorithm>
- #include <format>
- #include <iomanip>
- #include <windows.h>
- #include <iostream>
- #include <psapi.h>
- #include <string>
- #include <asmjit/asmjit.h>
- using namespace std;
- using namespace asmjit;
- using namespace x86;
- struct THREAD_PARAM {
- DWORD ret_value; // alloc_addr + 0x0
- FARPROC settimer; // alloc_addr + 0x4
- FARPROC killtimer; // alloc_addr + 0x8
- HWND remote_hwnd; // alloc_addr + 0xC
- DWORD timerproc; // alloc_addr + 0x10
- DWORD timer_id; // alloc_addr + 0x14
- DWORD custom_call; // alloc_addr + 0x18
- };
- /**
- * \brief 获取指定进程中的模块函数句柄
- * \param hRemoteProcess 指定进程句柄
- * \param lpModuleName 指定函数名所在的模块名,大小写任意
- * \param lpProcName 指定要获取的函数名,大小写必须匹配
- * \return 成功则返回指定的函数地址,未找到返回空指针
- * \note 可至微软learn网站查询函数所在的模块
- */
- FARPROC GetRemoteModule(_In_ const HANDLE hRemoteProcess, _In_ const LPCWSTR lpModuleName, _In_ const LPCSTR lpProcName) {
- // https://learn.microsoft.com/zh-cn/windows/win32/psapi/enumerating-all-modules-for-a-process
- // 获取远程模块信息
- HMODULE hModule[MAX_PATH];
- DWORD cbNeeded;
- if (!EnumProcessModules(hRemoteProcess, hModule, MAX_PATH, &cbNeeded))
- return nullptr;
- // 遍历枚举到的模块
- for (size_t i = 0; i < cbNeeded / sizeof(HMODULE); i++) {
- TCHAR szModName[MAX_PATH];
- // Get the full path to the module's file.
- if (GetModuleFileNameEx(hRemoteProcess, hModule[i], szModName,
- sizeof(szModName) / sizeof(TCHAR))) {
- // 转为wstring字符串,并全部转换为小写以便比较
- wstring ModName(szModName);
- wstring ModuleName(lpModuleName);
- ranges::transform(ModName, ModName.begin(), ::tolower);
- ranges::transform(ModuleName, ModuleName.begin(), ::tolower);
- //wcout << format(L"\t0x{:08X} {}\n", (DWORD)hModule[i], ModName);
- // 如果是要找的模块
- if (ModName.find(ModuleName) != wstring::npos)
- // 如果GetProcAddress函数成功,则返回值是导出的函数或变量的地址。如果GetProcAddress函数失败,则返回值为 NULL。 要获得更多的错误信息,请调用 GetLastError。
- return GetProcAddress(hModule[i], lpProcName);
- }
- }
- return nullptr;
- }
- int main() {
- const HWND remote_hwnd =
- FindWindow(L"Notepad", nullptr);
- cout << "remote_hwnd=" << remote_hwnd << endl;
- DWORD remote_pid = 0;
- GetWindowThreadProcessId(remote_hwnd, &remote_pid);
- cout << "remote_pid=" << remote_pid << endl;
- const HANDLE remote_handle = OpenProcess(PROCESS_ALL_ACCESS, false, remote_pid);
- if (!remote_handle)
- return 0;
- // 获取远程进程模块函数信息
- auto settimer = GetRemoteModule(remote_handle, TEXT("User32.dll"), "SetTimer");
- auto killtimer = GetRemoteModule(remote_handle, TEXT("User32.dll"), "KillTimer");
- //cout << format("SetTimer addr=0x{:08X}\n", (DWORD)settimer);
- //cout << format("KillTimer addr=0x{:08X}\n", (DWORD)killtimer);
- // 申请远程空间,写入和执行权限
- DWORD alloc_addr = (DWORD)VirtualAllocEx(remote_handle, nullptr, MAX_PATH, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
- if (!alloc_addr) {
- cout << "GetLastError()=" << GetLastError() << endl;
- return 0;
- }
- cout << setw(8) << setfill('0') << hex << "VirtualAlloc addr=0x" << alloc_addr << endl;
- DWORD addr_param = alloc_addr; // 参数内存地址
- DWORD addr_call = alloc_addr + 0x21; // 函数地址
- // 构造和写入函数
- THREAD_PARAM thread_param{};
- thread_param.remote_hwnd = remote_hwnd;
- thread_param.settimer = settimer;
- thread_param.killtimer = killtimer;
- // 生成代码
- JitRuntime rt;
- auto a = x86::Assembler();
- CodeHolder code;
- code.init(rt.environment(), rt.cpuFeatures());
- code.attach(&a);
- // 构造thread函数
- a.push(0);
- a.push(0);
- a.push(0);
- a.push(0);
- a.call(dword_ptr(alloc_addr + 0x4)); // settimer
- a.mov(dword_ptr(alloc_addr + 0x14), eax); //timer_id
- a.push(dword_ptr(alloc_addr + 0x10)); // timerproc
- a.push(0);
- a.push(eax);
- a.push(dword_ptr(alloc_addr + 0xC)); // remote_hwnd
- a.call(dword_ptr(alloc_addr + 0x4)); // settimer
- a.ret();
- CodeBuffer& thread = code.textSection()->buffer(); // 获取对应的机器码
- // 打印生成的机器码
- //cout << "assembly=";
- //for (auto& i : thread)
- // cout << format("{:02X} ", i);
- //cout << endl;
- size_t nSize = thread.size();
- cout << format("线程函数写入地址=0x{:08X} 大小=0x{:08X}\n", addr_call, nSize);
- WriteProcessMemory(remote_handle, (LPVOID)addr_call, thread.data(), nSize, nullptr);
- DWORD thread_addr = addr_call; // 记录线程函数地址
- addr_call = addr_call + nSize + 0x8;
- // 重置并复用CodeHolder变量code
- code.reset();
- code.init(rt.environment(), rt.cpuFeatures());
- code.attach(&a);
- // 构造timerproc
- a.push(dword_ptr(alloc_addr + 0x14)); // idTimer
- a.push(dword_ptr(alloc_addr + 0xC)); // hwnd
- a.call(dword_ptr(alloc_addr + 0x8)); // call KillTimer
- a.call(dword_ptr(alloc_addr + 0x18)); // custom_call
- a.ret();
- CodeBuffer& timerproc = code.textSection()->buffer();
- thread_param.timerproc = addr_call; // 记录therad地址
- nSize = timerproc.size();
- cout << format("回调函数写入地址=0x{:08X} 大小=0x{:08X}\n", addr_call, nSize);
- WriteProcessMemory(remote_handle, (LPVOID)addr_call, timerproc.data(), nSize, nullptr);
- addr_call = addr_call + nSize + 0x8;
- // 重置并复用CodeHolder变量code
- code.reset();
- code.init(rt.environment(), rt.cpuFeatures());
- code.attach(&a);
- // 构造自定函数
- a.xor_(eax, eax);
- a.div(eax); // 异常语句
- a.mov(dword_ptr(alloc_addr), eax); // 写入返回值到alloc_addr,程序读取alloc_addr地址的数值即可知道返回值
- a.ret();
- CodeBuffer& custom_call = code.textSection()->buffer();
- thread_param.custom_call = addr_call; // 记录custom_call地址
- nSize = custom_call.size();
- cout << format("自定函数写入地址=0x{:08X} 大小=0x{:08X}\n", addr_call, nSize);
- WriteProcessMemory(remote_handle, (LPVOID)addr_call, custom_call.data(), nSize, nullptr);
- addr_call = addr_call + nSize + 0x8;
- // 写入参数
- cout << format("线程参数写入地址=0x{:08X} 大小=0x{:08X}\n", addr_param, sizeof(THREAD_PARAM));
- WriteProcessMemory(remote_handle, (LPVOID)addr_param, &thread_param, sizeof(THREAD_PARAM), nullptr);
- const HANDLE hRemoteThread = CreateRemoteThread(remote_handle, nullptr, 0, (LPTHREAD_START_ROUTINE)thread_addr, (LPVOID)addr_param, 0, nullptr);
- //system("pause");
- return 0;
- }
复制代码
|
|