2012-02-17 21 views
11

Sono interessato all'aggancio e ho deciso di vedere se potevo agganciare alcune funzioni. Non mi interessava usare una libreria come le deviazioni perché voglio avere l'esperienza di farlo da solo. Con alcune fonti che ho trovato su internet, sono stato in grado di creare il codice qui sotto. È semplice, ma funziona bene. Tuttavia, quando si agganciano funzioni chiamate da più thread, risulta estremamente instabile. Se due chiamate vengono effettuate quasi alla stessa ora, si bloccherà. Dopo alcune ricerche penso di aver bisogno di creare una funzione trampolino. Dopo aver cercato per ore tutto ciò che non riuscivo a trovare altro che una descrizione generale su cosa fosse un trampolino. Non sono riuscito a trovare nulla di specifico sulla scrittura di una funzione di trampolino o su come funzionassero davvero. Se qualcuno può aiutarmi a scriverne uno, pubblicare alcune fonti, o almeno indirizzarmi nella giusta direzione, raccomandando alcuni articoli, siti, libri, ecc. Lo apprezzerei molto.Come creare una funzione trampolino per gancio

Di seguito è riportato il codice che ho scritto. È molto semplice, ma spero che altri possano imparare da esso.

test.cpp

#include "stdafx.h" 

Hook hook; 

typedef int (WINAPI *tMessageBox)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType); 

DWORD hMessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType) 
{ 
    hook.removeHook(); 
    tMessageBox oMessageBox = (tMessageBox)hook.funcPtr; 
    int ret =oMessageBox(hWnd, lpText, "Hooked!", uType); 
    hook.applyHook(&hMessageBox); 

    return ret; 
} 

void hookMessageBox() 
{ 
    printf("Hooking MessageBox...\n"); 
    if(hook.findFunc("User32.dll", "MessageBoxA")) 
    { 
     if(hook.applyHook(&hMessageBox)) 
     { 
      printf("hook applied! \n\n"); 
     } else printf("hook could not be applied\n"); 
    } 
} 

Hook.cpp

#include "stdafx.h" 

bool Hook::findFunc(char* libName, char* funcName) 
{ 
    Hook::funcPtr = (void*)GetProcAddress(GetModuleHandleA(libName), funcName); 
    return (Hook::funcPtr != NULL); 
} 

bool Hook::removeHook() 
{ 
    DWORD dwProtect; 
    if(VirtualProtect(Hook::funcPtr, 6, PAGE_EXECUTE_READWRITE, &dwProtect)) 
     { 
     WriteProcessMemory(GetCurrentProcess(), (LPVOID)Hook::funcPtr, Hook::origData, 6, 0); 
     VirtualProtect(Hook::funcPtr, 6, dwProtect, NULL); 
     return true; 
    } else return false; 
} 

bool Hook::reapplyHook() 
{ 
    DWORD dwProtect; 
    if(VirtualProtect(funcPtr, 6, PAGE_EXECUTE_READWRITE, &dwProtect)) 
     { 
     WriteProcessMemory(GetCurrentProcess(), (LPVOID)funcPtr, Hook::hookData, 6, 0); 
     VirtualProtect(funcPtr, 6, dwProtect, NULL); 
     return true; 
    } else return false; 
} 

bool Hook::applyHook(void* hook) 
{ 
    return setHookAtAddress(Hook::funcPtr, hook); 
} 

bool Hook::setHookAtAddress(void* funcPtr, void* hook) 
{ 
    Hook::funcPtr = funcPtr; 
    BYTE jmp[6] = { 0xE9, //jmp 
        0x00, 0x00, 0x00, 0x00, //address 
        0xC3 //retn 
       }; 

    DWORD dwProtect; 

    if(VirtualProtect(funcPtr, 6, PAGE_EXECUTE_READWRITE, &dwProtect)) // make memory writable 
    { 

     ReadProcessMemory(GetCurrentProcess(), (LPVOID)funcPtr, Hook::origData, 6, 0); // save old data 
     DWORD offset = ((DWORD)hook - (DWORD)funcPtr - 5); //((to)-(from)-5) 
     memcpy(&jmp[1], &offset, 4); // write address into jmp 
     memcpy(Hook::hookData, jmp, 6); // save hook data 
     WriteProcessMemory(GetCurrentProcess(), (LPVOID)funcPtr, jmp, 6, 0); // write jmp 
     VirtualProtect(funcPtr, 6, dwProtect, NULL); // reprotect 

     return true; 
    } else return false; 
} 
+0

Stavo per pubblicare un collegamento a GD, ma ho notato che anche tu sei un membro. Hai provato a usare la loro funzione di ricerca? Ne vengono fuori tonnellate di esempi :) –

+0

Puoi controllare il codice di EasyHook, credo che sia open source. Ci sono anche molti altri esempi in giro. Se hai intenzione di usarlo in un'applicazione distribuita, ti consiglio di utilizzare una libreria (come EasyHook) che può già gestire la ricorsione su gancio/trampolino, threading e alcune delle cose divertenti. – ssube

+0

@Tom Knapen Ho cercato GD, MPGH e alcuni altri siti prima di pubblicare. La ricerca di 'trampolino' su GD restituisce alcuni post leggermente correlati ma non quello che sto cercando. – Stratus

risposta

7

Se si desidera che il gancio per essere sicuri quando viene chiamato da più thread, non si vuole essere costantemente sgancio e manovra di riaggancio della API originale.

Un trampolino è semplicemente un po 'di codice generato che replica la funzionalità dei primi pochi byte dell'API originale (che hai sovrascritto con il salto), quindi salta nell'API dopo i byte che hai sovrascritto.

Invece di sganciare l'API, chiamandola e riprogrammandola, si chiama semplicemente il trampolino.

Questo è moderatamente complicato da eseguire su x86 perché è necessario (un disassemblatore abbastanza minimale) per trovare i limiti delle istruzioni. È inoltre necessario verificare che il codice copiato sul trampolino non faccia nulla rispetto al puntatore dell'istruzione (come jmp, ramo o chiamata).

Questo è sufficiente per rendere le chiamate al gancio sicure, ma non è possibile creare il gancio se più thread utilizzano l'API. Per questo, è necessario agganciare la funzione con un salto vicino a due byte (che può essere scritto atomicamente). Le API di Windows sono spesso precedute da alcuni NOP (che possono essere sovrascritti con un salto in alto) per fornire un obiettivo per questo quasi salto.

Fare questo su x64 è molto più complicato. Non puoi semplicemente applicare patch alla funzione con un salto lontano a 64 bit (perché non ce n'è uno, e le istruzioni per simularlo sono spesso troppo lunghe). E, a seconda di cosa fa il tuo trampolino, potrebbe essere necessario aggiungerlo alle informazioni di srotolamento dello stack del sistema operativo.

Spero che non sia troppo generico.

+0

Grazie, ma questo era praticamente quello che ho già trovato e non mi aiuta a scriverne uno. – Stratus

+0

@Stratus: cosa ti manca? Assegna memoria eseguibile. Copia n byte dalla funzione prolog alla memoria allocata e seguila con un salto per eseguire prolog + n. n è la dimensione delle istruzioni che è necessario copiare per liberare almeno 5 byte nel prologo della funzione. Quello è un trampolino. Ci sono altre rughe (come non copiare le istruzioni che modificano l'IP), ma questo è basicamente. – arx

Problemi correlati