我有没有源代码的应用程序使用代理DLL库直接写入设备只执行一件事,通过调用导出函数返回内存指针" GetDataPointer"。我想替换这个库,看看每次写入到接收内存之间的变化。内存大小是页面大小的倍数(4096)。
我已经这样试过了:
一切都会好的,但是这个应用程序使用了写入这个内存指针的线程。让我们说应用程序在主线程中向该内存指针写入三次,在它之间生成对该指针进行三次写入的其他线程。虽然没有线程,但这可以工作,有线程的时候它会错过页面保护,所以它直接写入内存指针而不会引发异常。因此异常处理程序应捕获对内存的六次写入,但由于缺少页面保护,该数字较低。遗憾的是,我需要这样做,我需要100%的准确性,因为我计划为一个模糊的API制作一个包装器,以便更加模糊的设备(图形卡)在现代系统中使用这个应用程序。
我已尝试在每个连接的线程中使用全局关键部分并为SEH设置异常处理程序,但仍然存在问题。
是否有可能解决此问题?或者有更好的方法来实现它吗?如果有人需要代码来解释它是如何工作的,我可以准备代码来模拟我的工作环境(应用程序和库)。
提前谢谢。
更新
没有锁定部分的源代码(伪代码)看起来像这样(这篇文章匆匆写完,可能不完整):
应用:
extern "C" __declspec(dllimport) void* GetDataPointer(int size);
extern "C" __declspec(dllimport) int GetCallCount();
#define THREAD_COUNT 32
DWORD WINAPI ThreadEntryPoint(void* param) {
unsigned int* data = (unsigned int*)param;
data[0] = 0xEEFF0011;
return 0;
}
int main(int argc, char* argv[]) {
unsigned int* data = (unsigned int*)GetDataPointer();
std::vector<HANDLE> threads;
data[0] = 0xAABBCCDD;
for(int i = 0; i<THREAD_COUNT; i++) {
DWORD threadID;
HANDLE hThread = CreateThread(NULL,0,ThreadEntryPoint,(void*)data,0,&threadID);
threads.push_back(hThread);
}
for(int i = 0; i<threads.size(); i++)
WaitForSingleObject(threads.at(i),INFINITE);
printf("Should be %i calls, was %i.\n",(THREAD_COUNT+1),GetCallCount());
return 0;
}
库:
#define DATA_SIZE 4096
void* data;
int callCount;
LONG WINAPI ExceptionHandler(LPEXCEPTION_POINTERS ExceptionInfo) {
LONG ret = EXCEPTION_CONTINUE_SEARCH;
switch(ExceptionInfo->ExceptionRecord->ExceptionCode) {
case STATUS_GUARD_PAGE_VIOLATION: {
ExceptionInfo->ContextRecord->EFlags |= 0x100;
ret = EXCEPTION_CONTINUE_EXECUTION;
break;
}
case EXCEPTION_SINGLE_STEP: {
DWORD old;
callCount++;
VirtualProtect(data,DATA_SIZE,PAGE_READWRITE,&old);
// Find what was changed in data...
VirtualProtect(data,DATA_SIZE,PAGE_GUARD | PAGE_READWRITE,&old);
ret = EXCEPTION_CONTINUE_EXECUTION;
break;
}
}
return ret;
}
extern "C" void* __declspec(dllexport) GetDataPointer() {
data = (void*)VirtualAlloc((PVOID)data,DATA_SIZE,MEM_RESERVE | MEM_COMMIT,PAGE_READWRITE);
memset(data,0,DATA_SIZE);
DWORD old;
VirtualProtect(data,DATA_SIZE,PAGE_GUARD | PAGE_READWRITE,&old);
return data;
}
extern "C" int __declspec(dllexport) GetCallCount() {
return callCount;
}
extern "C" BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
switch(fdwReason) {
case DLL_PROCESS_ATTACH: {
data = NULL;
callCount = 0;
SetUnhandledExceptionFilter(ExceptionHandler);
break;
}
case DLL_PROCESS_DETACH: {
if(data)
VirtualFree(data,data_size,MEM_RELEASE | MEM_DECOMMIT);
break;
}
case DLL_THREAD_ATTACH: {
SetUnhandledExceptionFilter(ExceptionHandler);
break;
}
case DLL_THREAD_DETACH: {
break;
}
}
return TRUE;
}
我希望这会更清楚。
答案 0 :(得分:0)
可以给出建议写迷你调试器,它将调试你的主应用程序。而调试器处理来自线程的异常 - 进程中的所有其他线程都被挂起。这是关键点。当你处理STATUS_GUARD_PAGE_VIOLATION时 - 你需要为所有线程调用SuspendThread,除了当前 - 在调试器中没有问题维护所有线程的列表。或者作为当前线程的替代调用ZwSuspendProcess和ZwResumeThread。然后在当前线程和DBG_CONTINUE中设置TF标志(0x100)。然后当你得到STATUS_SINGLE_STEP异常(或STATUS_WX86_SINGLE_STEP)时,你为除当前之外的所有线程恢复PAGE_GUARD和ResumeThread。 (或简单地调用ZwResumeProcess)。这当然不仅仅是任务,但只有这样才能100%保证您不会错过任何跟踪的内存参考