2016-08-24 2 views
0

J'ai une application sans code source qui écrit directement sur un périphérique avec l'utilisation d'une bibliothèque DLL proxy qui ne fait qu'une chose, renvoie le pointeur mémoire en appelant la fonction exportée "GetDataPointer" . Je voudrais remplacer cette bibliothèque et voir les changements entre chaque écriture dans la mémoire reçue. La taille de la mémoire est multiple de la taille de la page (4096).Utilisation de gestion des exceptions structurées (SEH) pour surveiller l'utilisation des pages protégées

Je l'ai essayé de cette façon:

  1. dans DllMain configuration du gestionnaire d'exception via SetUnhandledExceptionFilter
  2. en mémoire GetDataPointer alloc via VirtualAlloc, définissez la page de garde
  3. dans le gestionnaire d'exception retirer la protection de la page, comparer les données avec copie précédente, sécuriser la page

Tout irait bien, mais cette application utilise des threads qui écrivent sur ce pointeur de mémoire. Supposons que l'application écrit trois fois sur ce pointeur de mémoire dans le thread principal, entre deux threads qui génère trois écritures sur ce pointeur. Bien que sans threads, cela fonctionnera, avec les threads, il y a un moment où le gardiennage de pages manque, de sorte qu'il écrit directement sur le pointeur de la mémoire sans déclencher d'exception. Par conséquent, le gestionnaire d'exceptions doit capturer six écritures en mémoire, mais en raison du manque de garde de page, le nombre est inférieur. Malheureusement, je dois le faire de cette façon et j'ai besoin de 100% de précision, parce que je prévois de faire un emballage pour une API obscure pour un appareil encore plus obscur (carte graphique) pour utiliser cette application dans les systèmes modernes.

J'ai essayé d'utiliser la section critique globale et de définir le gestionnaire d'exceptions pour SEH dans chaque thread attaché, mais le problème persiste.

Y at-il une possibilité de contourner ce problème? Ou y a-t-il une meilleure façon de mettre en œuvre cela? Si quelqu'un a besoin de code pour expliquer comment cela fonctionne, je peux préparer du code qui simulera mon environnement de travail (application et bibliothèque).

Merci à l'avance.

Mise à jour

code source (pseudo-code) sans sections de verrouillage ressemble à ceci (écrit pressé pour ce poste, peut être pas terminé):

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; 
} 

Bibliothèque :

#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; 
} 

J'espère que ce sera plus clair.

+1

Vous avez besoin d'une exception pour chaque écriture pour le faire fonctionner correctement? Profil ce code avant de faire autre chose. –

+0

@ hans-passant Oui, j'ai besoin de connaître chaque écriture de mémoire pour que cela fonctionne correctement. Le morceau de mémoire peut être interprété comme une structure et j'ai besoin de savoir quels éléments ont été modifiés. Par exemple, la première écriture serait une commande qui est écrite à l'un des quatre décalages statiques et l'écriture captée suivante serait des paramètres qui ne sont pas des décalages statiques, j'ai donc besoin de savoir ce qui a été changé et quelle valeur il a. Plus encore, les paramètres des commandes peuvent être écrits deux fois de suite au même décalage (écrasés). –

+0

La façon dont je lis les documents pour SetUnhandledExceptionFilter est qu'il piège uniquement les exceptions non interceptées. Donc, si un thread dans l'application attrape d'abord l'exception, vous ne le verrez pas. – Brad

Répondre

-1

peut donner des conseils écrire mini-débogueur, qui sera déboguer votre application principale. tandis que le débogueur gère l'exception du thread - tous les autres threads en cours sont suspendus. C'est un point clé. Lorsque vous manipulez STATUS_GUARD_PAGE_VIOLATION - vous devez appeler SuspendThread pour tous les threads, sauf current - in debugger pas de problème pour maintenir la liste de tous les threads. ou comme alternative appelez ZwSuspendProcess et ZwResumeThread pour le thread en cours. puis définissez l'indicateur TF (0x100) dans le thread en cours et DBG_CONTINUE. puis, lorsque vous avez une exception STATUS_SINGLE_STEP (ou STATUS_WX86_SINGLE_STEP), vous restaurez PAGE_GUARD et ResumeThread pour tous les threads sauf current. (ou appelez simplement ZwResumeProcess).ceci peut bien sûr ne pas être simplement une tâche, mais seulement ceci vous donne 100% de garantie que vous ne manquez aucune référence mémoire suivie