gpt4 book ai didi

windows - 如何使用写入地址捕获内存写入和调用函数

转载 作者:IT王子 更新时间:2023-10-28 23:36:57 25 4
gpt4 key购买 nike

我想捕获对特定内存范围的内存写入,并使用正在写入的内存位置的地址调用一个函数。最好是在写入内存已经发生之后。

我知道这可以由操作系统通过调整页表条目来完成。但是,如何在想要执行此操作的应用程序中实现类似的操作?

最佳答案

好吧,你可以这样做:

// compile with Open Watcom 1.9: wcl386 wrtrap.c

#include <windows.h>
#include <stdio.h>

#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
#endif


UINT_PTR RangeStart = 0;
SIZE_T RangeSize = 0;

UINT_PTR AlignedRangeStart = 0;
SIZE_T AlignedRangeSize = 0;


void MonitorRange(void* Start, size_t Size)
{
DWORD dummy;

if (Start &&
Size &&
(AlignedRangeStart == 0) &&
(AlignedRangeSize == 0))
{
RangeStart = (UINT_PTR)Start;
RangeSize = Size;

// Page-align the range address and size

AlignedRangeStart = RangeStart & ~(UINT_PTR)(PAGE_SIZE - 1);

AlignedRangeSize = ((RangeStart + RangeSize - 1 + PAGE_SIZE) &
~(UINT_PTR)(PAGE_SIZE - 1)) -
AlignedRangeStart;

// Make the page range read-only
VirtualProtect((LPVOID)AlignedRangeStart,
AlignedRangeSize,
PAGE_READONLY,
&dummy);
}
else if (((Start == NULL) || (Size == 0)) &&
AlignedRangeStart &&
AlignedRangeSize)
{
// Restore the original setting
// Make the page range read-write
VirtualProtect((LPVOID)AlignedRangeStart,
AlignedRangeSize,
PAGE_READWRITE,
&dummy);

RangeStart = 0;
RangeSize = 0;

AlignedRangeStart = 0;
AlignedRangeSize = 0;
}
}

// This is where the magic happens...
int ExceptionFilter(LPEXCEPTION_POINTERS pEp,
void (*pMonitorFxn)(LPEXCEPTION_POINTERS, void*))
{
CONTEXT* ctx = pEp->ContextRecord;
ULONG_PTR* info = pEp->ExceptionRecord->ExceptionInformation;
UINT_PTR addr = info[1];
DWORD dummy;

switch (pEp->ExceptionRecord->ExceptionCode)
{
case STATUS_ACCESS_VIOLATION:
// If it's a write to read-only memory,
// to the pages that we made read-only...
if ((info[0] == 1) &&
(addr >= AlignedRangeStart) &&
(addr < AlignedRangeStart + AlignedRangeSize))
{
// Restore the original setting
// Make the page range read-write
VirtualProtect((LPVOID)AlignedRangeStart,
AlignedRangeSize,
PAGE_READWRITE,
&dummy);

// If the write is exactly within the requested range,
// call our monitoring callback function
if ((addr >= RangeStart) && (addr < RangeStart + RangeSize))
{
pMonitorFxn(pEp, (void*)addr);
}

// Set FLAGS.TF to trigger a single-step trap after the
// next instruction, which is the instruction that has caused
// this page fault (AKA access violation)
ctx->EFlags |= (1 << 8);

// Execute the faulted instruction again
return EXCEPTION_CONTINUE_EXECUTION;
}

// Don't handle other AVs
goto ContinueSearch;

case STATUS_SINGLE_STEP:
// The instruction that caused the page fault
// has now succeeded writing to memory.
// Make the page range read-only again
VirtualProtect((LPVOID)AlignedRangeStart,
AlignedRangeSize,
PAGE_READONLY,
&dummy);

// Continue executing as usual until the next page fault
return EXCEPTION_CONTINUE_EXECUTION;

default:
ContinueSearch:
// Don't handle other exceptions
return EXCEPTION_CONTINUE_SEARCH;
}
}


// We'll monitor writes to blah[1].
// volatile is to ensure the memory writes aren't
// optimized away by the compiler.
volatile int blah[3] = { 3, 2, 1 };

void WriteToMonitoredMemory(void)
{
blah[0] = 5;
blah[0] = 6;
blah[0] = 7;
blah[0] = 8;

blah[1] = 1;
blah[1] = 2;
blah[1] = 3;
blah[1] = 4;

blah[2] = 10;
blah[2] = 20;
blah[2] = 30;
blah[2] = 40;
}

// This pointer is an attempt to ensure that the function's code isn't
// inlined. We want to see it's this function's code that modifies the
// monitored memory.
void (* volatile pWriteToMonitoredMemory)(void) = &WriteToMonitoredMemory;

void WriteMonitor(LPEXCEPTION_POINTERS pEp, void* Mem)
{
printf("We're about to write to 0x%X from EIP=0x%X...\n",
Mem,
pEp->ContextRecord->Eip);
}

int main(void)
{
printf("&WriteToMonitoredMemory() = 0x%X\n", pWriteToMonitoredMemory);
printf("&blah[1] = 0x%X\n", &blah[1]);

printf("\nstart\n\n");

__try
{
printf("blah[0] = %d\n", blah[0]);
printf("blah[1] = %d\n", blah[1]);
printf("blah[2] = %d\n", blah[2]);

// Start monitoring memory writes
MonitorRange((void*)&blah[1], sizeof(blah[1]));

// Write to monitored memory
pWriteToMonitoredMemory();

// Stop monitoring memory writes
MonitorRange(NULL, 0);

printf("blah[0] = %d\n", blah[0]);
printf("blah[1] = %d\n", blah[1]);
printf("blah[2] = %d\n", blah[2]);
}
__except(ExceptionFilter(GetExceptionInformation(),
&WriteMonitor)) // write monitor callback function
{
// never executed
}

printf("\nstop\n");
return 0;
}

输出(在 Windows XP 上运行):

&WriteToMonitoredMemory() = 0x401179
&blah[1] = 0x4080DC

start

blah[0] = 3
blah[1] = 2
blah[2] = 1
We're about to write to 0x4080DC from EIP=0x4011AB...
We're about to write to 0x4080DC from EIP=0x4011B5...
We're about to write to 0x4080DC from EIP=0x4011BF...
We're about to write to 0x4080DC from EIP=0x4011C9...
blah[0] = 8
blah[1] = 4
blah[2] = 40

stop

就是这样。

您可能需要进行一些更改以使代码在多个线程中正常工作,使其与其他 SEH 代码(如果有)以及 C++ 异常(如果适用)一起工作。

当然,如果你真的想要它,你可以让它在写入完成后调用写入监控回调函数。为此,您需要将 STATUS_ACCESS_VIOLATION 案例中的内存地址保存在某处(TLS?),以便 STATUS_SINGLE_STEP 案例可以获取它稍后传递给函数。

关于windows - 如何使用写入地址捕获内存写入和调用函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8004945/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com