Abyx Asked:2020-06-27 00:12:22 +0000 UTC2020-06-27 00:12:22 +0000 UTC 2020-06-27 00:12:22 +0000 UTC 如何以编程方式在内存中设置断点? 772 进行一个读取bool变量的循环 #include <cstdio> volatile bool stop = false; void loop() { while (!stop) { putchar('.'); } } 我们想更改stop第 4 次读取时的值,以便循环打印省略号并结束。 如何拦截 Windows 上停止变量的读数并更改其值? c++ 1 个回答 Voted Best Answer Abyx 2020-06-27T00:12:22Z2020-06-27T00:12:22Z 对于内存断点,可以使用Guard Page。这是一个特殊的内存页面属性标志,其中页面访问会引发异常STATUS_GUARD_PAGE_VIOLATION (0x80000001)。在这种情况下,Guard Page flag 被清除,重复访问内存页不会导致异常。 对于内存读取,异常处理程序可以捕获异常STATUS_GUARD_PAGE_VIOLATION,更改内存的值,并将异常标记为已处理。读取将重新开始,并且将读取处理程序中设置的新值。 为了防止断点是一次性的,必须重新设置Guard Page标志。为此,STATUS_GUARD_PAGE_VIOLATION在处理程序中设置处理器标志 TF(陷阱标志)。然后在执行下一条指令之前会抛出异常STATUS_SINGLE_STEP。在它的处理程序中,您可以更新保护页面标志,如果它进行了内存写入,您还可以读取前一条指令写入的值。 在代码中它看起来像这样: #include <windows.h> int aux_counter = 0; // счетчик для отсчета четырех чтений // установка PAGE_GUARD для страницы в которую входит адрес |addr| void set_guard_on_bool(volatile bool* addr) { MEMORY_BASIC_INFORMATION info; VirtualQuery((void*)addr, &info, sizeof(info)); DWORD unused; VirtualProtect((void*)addr, sizeof(*addr), info.Protect | PAGE_GUARD, &unused); } // обработчик исключений LONG __stdcall exception_handler(EXCEPTION_POINTERS* e) { switch (e->ExceptionRecord->ExceptionCode) { case STATUS_GUARD_PAGE_VIOLATION: { // параметры исключения - чтение/запись и адрес памяти bool is_read = e->ExceptionRecord->ExceptionInformation[0] == 0; void* location = (void*)e->ExceptionRecord->ExceptionInformation[1]; // проверяем что это чтение адреса |stop| // TODO: тут можно пропустить исключения для всех остальных страниц памяти if (location == &stop && is_read) { ++aux_counter; // завершаем цикл на 4-е чтение переменной if (aux_counter == 4) { stop = true; return EXCEPTION_CONTINUE_EXECUTION; } } // выставляем TF чтобы вызвать исключение STATUS_SINGLE_STEP e->ContextRecord->EFlags |= 0x100; return EXCEPTION_CONTINUE_EXECUTION; } case STATUS_SINGLE_STEP: { // обновляем флаг PAGE_GUARD set_guard_on_bool(&stop); return EXCEPTION_CONTINUE_EXECUTION; } default: { // пропускаем остальные исключения return EXCEPTION_CONTINUE_SEARCH; } } } int main() { // устанавливаем обработчик исключений AddVectoredExceptionHandler(1 /* first */, exception_handler); // и выставляем флаг PAGE_GUARD set_guard_on_bool(&stop); loop(); } 程序运行示例: > g++ -O3 main.cpp && a.exe ...
对于内存断点,可以使用Guard Page。这是一个特殊的内存页面属性标志,其中页面访问会引发异常
STATUS_GUARD_PAGE_VIOLATION (0x80000001)。在这种情况下,Guard Page flag 被清除,重复访问内存页不会导致异常。对于内存读取,异常处理程序可以捕获异常
STATUS_GUARD_PAGE_VIOLATION,更改内存的值,并将异常标记为已处理。读取将重新开始,并且将读取处理程序中设置的新值。为了防止断点是一次性的,必须重新设置Guard Page标志。为此,
STATUS_GUARD_PAGE_VIOLATION在处理程序中设置处理器标志 TF(陷阱标志)。然后在执行下一条指令之前会抛出异常STATUS_SINGLE_STEP。在它的处理程序中,您可以更新保护页面标志,如果它进行了内存写入,您还可以读取前一条指令写入的值。在代码中它看起来像这样:
程序运行示例: