I have application without source code that makes direct write to device with use of proxy DLL library that only does one thing, returns memory pointer by calling exported function "GetDataPointer". I would like to make a replacement for this library and see changes between each write to received memory. Memory size is multiple of page size (4096).
I've tried it this way:
- in DllMain set exception handler via SetUnhandledExceptionFilter
- in GetDataPointer alloc memory via VirtualAlloc, set guard page
- in exception handler remove page guard, compare data with previous copy, make page guarded
Everything would be ok, but this application makes use of threads which writes to this memory pointer. Let's say application writes three times to this memory pointer in main thread, in between spawns other thread that makes three writes to this pointer. While without threads this will work, with threads there is a time when it does miss page guarding so it writes directly to memory pointer without raising exception. So exception handler should capture six writes to memory, but because of missing page guard the number is lower. Sadly, I need to do it this way and I need 100% accuracy, because I'm planning to make a wrapper for a obscure API for even more obscure device (graphics card) to make use of this application in modern systems.
I've tried making use of global critical section and setting exception handler for SEH in each attached thread, but still problem remains.
Is there a possibility to workaround this problem? Or is there a better way to implement this? If anybody needs code to explain how this works, I can prepare code which will simulate my working environment (application and library).
Thank you in advance.
Update
Source code (pseudocode) without locking sections looks like this (written in hurry for this post, may be not complete):
Application:
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;
}
Library:
#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;
}
I hope this will be more clear.