Hooking
Inom datorprogrammering täcker termen hooking en rad tekniker som används för att ändra eller förstärka beteendet hos ett operativsystem , applikationer eller andra programvarukomponenter genom att avlyssna funktionsanrop eller meddelanden eller händelser som skickas mellan programvarukomponenter . Kod som hanterar sådana avlyssnade funktionssamtal, händelser eller meddelanden kallas en hook .
Hooking används för många ändamål, inklusive felsökning och utökad funktionalitet. Exempel kan inkludera avlyssning av tangentbords- eller mushändelsemeddelanden innan de når ett program, eller avlyssning av operativsystemanrop för att övervaka beteendet eller modifiera funktionen hos en applikation eller annan komponent. Det används också flitigt i benchmarkingprogram, till exempel bildhastighetsmätning i 3D-spel, där utdata och inmatning sker genom hooking.
Hooking kan också användas av skadlig kod. Till exempel använder rootkits , programvaror som försöker göra sig osynliga genom att fejka utdata från API- anrop som annars skulle avslöja deras existens, ofta hooking-tekniker.
Metoder
Vanligtvis sätts krokar in medan programvaran redan körs, men hooking är en taktik som också kan användas innan applikationen startas. Båda dessa tekniker beskrivs mer i detalj nedan.
Källändring
Hooking kan uppnås genom att modifiera källan till den körbara filen eller biblioteket innan en applikation körs, genom tekniker för reverse engineering . Detta används vanligtvis för att avlyssna funktionsanrop för att antingen övervaka eller ersätta dem helt.
, genom att använda en disassembler , kan ingångspunkten för en funktion i en modul hittas. Den kan sedan ändras för att istället dynamiskt ladda någon annan biblioteksmodul och sedan få den att exekvera önskade metoder inom det laddade biblioteket. Om tillämpligt, är en annan relaterad tillvägagångssätt genom vilken hooking kan uppnås genom att ändra importtabellen för en körbar fil. Denna tabell kan modifieras för att ladda eventuella ytterligare biblioteksmoduler samt ändra vilken extern kod som anropas när en funktion anropas av applikationen.
En alternativ metod för att uppnå funktionskoppling är genom att avlyssna funktionsanrop genom ett omslagsbibliotek . En wrapper är en version av ett bibliotek som ett program laddar, med samma funktionalitet som det ursprungliga biblioteket som det kommer att ersätta. Det vill säga att alla funktioner som är tillgängliga är i huvudsak desamma mellan originalet och ersättningen. Det här omslagsbiblioteket kan designas för att anropa alla funktioner från det ursprungliga biblioteket, eller ersätta det med en helt ny uppsättning logik.
Körtidsändring
Operativsystem och programvara kan ge möjlighet att enkelt infoga händelsekrokar under körning . Det är tillgängligt förutsatt att processen för att sätta in kroken ges tillräckligt med tillstånd för att göra det. Microsoft Windows tillåter till exempel användare att infoga krokar som kan användas för att bearbeta eller ändra systemhändelser och programhändelser för dialoger , rullningslister och menyer samt andra objekt. Det tillåter också en krok att infoga, ta bort, bearbeta eller ändra tangentbords- och mushändelser . Linux tillhandahåller ett annat exempel där krokar kan användas på liknande sätt för att bearbeta nätverkshändelser inom kärnan genom NetFilter .
När sådan funktionalitet inte tillhandahålls, använder en speciell form av hooking avlyssning av biblioteksfunktionsanrop som görs av en process. Funktionshooking implementeras genom att ändra de allra första kodinstruktionerna för målfunktionen för att hoppa till en injicerad kod. Alternativt på system som använder det delade bibliotekskonceptet kan avbrottsvektortabellen eller importdeskriptortabellen modifieras i minnet. Dessa taktiker använder i huvudsak samma idéer som källmodifiering, men ändrar istället instruktioner och strukturer som finns i minnet av en process när den redan körs.
Exempelkod
Virtuell metod bordskrokning
Närhelst en klass definierar/ärver en virtuell funktion (eller metod), lägger kompilatorer till en dold medlemsvariabel till klassen som pekar på en virtuell metodtabell (VMT eller Vtable). De flesta kompilatorer placerar den dolda VMT-pekaren vid de första 4 byten av varje instans av klassen. En VMT är i grunden en samling pekare till alla virtuella funktioner som instanser av klassen kan anropa. Vid körning är dessa pekare inställda på att peka på rätt funktioner, eftersom det vid kompilering ännu inte är känt om basfunktionen ska anropas eller om en åsidosatt version av funktionen från en härledd klass ska anropas (vilket tillåter för polymorfism ). Därför kan virtuella funktioner kopplas genom att ersätta pekarna till dem i vilken VMT som helst som de visas. Koden nedan visar ett exempel på en typisk VMT-hook i Microsoft Windows, skriven i C++.
0
0
#inkludera <iostream> #inkludera "windows.h" med namnutrymme std ; class VirtualClass { public : int number ; virtual void VirtualFn1 ( ) //Detta är den virtuella funktionen som kommer att kopplas. { cout << "VirtualFn1 kallas " << nummer ++ << " \n\n " ; } }; använder VirtualFn1_t = void ( __thiscall * )( void * thisptr ); VirtualFn1_t orig_VirtualFn1 ; void __fastcall hkVirtualFn1 ( void * thisptr , int edx ) //Detta är vår hook-funktion som vi kommer att få programmet att anropa istället för den ursprungliga VirtualFn1-funktionen efter att hooking är klar. { cout << "Hook-funktion anropad" << " \n " ; orig_VirtualFn1 ( thisptr ); //Anrop originalfunktionen. } int main () { VirtualClass * myClass = new VirtualClass (); //Skapa en pekare till en dynamiskt allokerad instans av VirtualClass. void ** vTablePtr = * reinterpret_cast < void ***> ( myClass ); //Hitta adressen som pekar mot basen av VirtualClass VMT (som sedan pekar på VirtualFn1) och lagra den i vTablePtr. DWORD oldProtection ; VirtualProtect ( vTablePtr , 4 , PAGE_EXECUTE_READWRITE , & oldProtection ); //Tar bort sidskydd i början av VMT så att vi kan skriva över dess första pekare. orig_VirtualFn1 = reinterpret_cast < VirtualFn1_t > ( * vTablePtr ) ; //Lagrar pekaren till VirtualFn1 från VMT i en global variabel så att den kan nås igen senare efter att dess inmatning i VMT har // skrivits över med vår hook-funktion. * vTablePtr = & hkVirtualFn1 ; //Skriv över pekaren till VirtualFn1 i den virtuella tabellen till en pekare till vår hook-funktion (hkVirtualFn1). VirtualProtect ( vTablePtr , 4 , oldProtection , ); //Återställ gammalt sidskydd. myClass -> VirtualFn1 (); //Anrop den virtuella funktionen från vår klassinstans. Eftersom den nu är ansluten kommer detta faktiskt att anropa vår hook-funktion (hkVirtualFn1). myClass -> VirtualFn1 (); myClass -> VirtualFn1 (); ta bort myClass ; återvända ; }
Det är viktigt att notera att alla virtuella funktioner måste vara klassmedlemsfunktioner, och alla (icke-statiska) klassmedlemsfunktioner anropas med anropskonventionen __thiscall (såvida inte medlemsfunktionen tar ett variabelt antal argument, i vilket fall den anropas med __cdecl). __thiscall-anropskonventionen skickar en pekare till den anropande klassinstansen (vanligtvis kallad "denna"-pekare) via ECX-registret (på x86-arkitekturen). Därför, för att en krokfunktion ska kunna fånga upp "den här" pekaren som skickas och ta den som ett argument, måste den titta in i ECX-registret. I exemplet ovan görs detta genom att ställa in hook-funktionen (hkVirtualFn1) att använda __fastcall-anropskonventionen, vilket gör att hook-funktionen letar in i ECX-registret efter ett av dess argument.
Observera också att i exemplet ovan är hook-funktionen (hkVirtualFn1) inte en medlemsfunktion i sig så den kan inte använda __thiscall-anropskonventionen. __fastcall måste användas istället eftersom det är den enda andra anropskonventionen som letar efter ett argument i ECX-registret.
C# keyboard event hook
Följande exempel kopplas in i tangentbordshändelser i Microsoft Windows med hjälp av Microsoft .NET Framework .
0
0
0
0
0
0
0
använder System.Runtime.InteropServices ; namnutrymme Krokar ; public class KeyHook { /* Medlemsvariabler */ protected static int Hook ; skyddad statisk LowLevelKeyboardDelegate Delegate ; skyddat statiskt skrivskyddat objekt Lås = nytt objekt (); skyddad statisk bool IsRegistered = false ; /* DLL-import */ [DllImport("user32")] privat statisk extern int SetWindowsHookEx ( int idHook , LowLevelKeyboardDelegate lpfn , int hmod , int dwThreadId ); [DllImport("user32")] privat statisk extern int CallNextHookEx ( int hHook , int nCode , int wParam , KBDLLHOOKSTRUCT lParam ); [DllImport("user32")] privat statisk extern int UnhookWindowsHookEx ( int hHook ); /* Typer & konstanter */ protected delegate int LowLevelKeyboardDelegate ( int nCode , int wParam , ref KBDLLHOOKSTRUCT lParam ); privat konst int HC_ACTION = ; privat konst int WM_KEYDOWN = x0100 ; privat konst int WM_KEYUP = x0101 ; privat konst int WH_KEYBOARD_LL = 13 ; [StructLayout(LayoutKind.Sequential)] public struct KBDLLHOOKSTRUCT { public int vkCode ; public int scanCode ; offentliga int- flaggor ; offentlig int tid ; public int dwExtraInfo ; } /* Metoder */ statisk privat int LowLevelKeyboardHandler ( int nCode , int wParam , ref KBDLLHOOKSTRUCT lParam ) { if ( nCode == HC_ACTION ) { if ( wParam == WM_KEYDOWN ) System . Konsol . Ut . WriteLine ( "Key Down: " + lParam . vkCode ); annat om ( wParam == WM_KEYUP ) System . Konsol . Ut . WriteLine ( "Key Up: " + lParam . vkCode ); } returnera CallNextHookEx ( Hook , nCode , wParam , lParam ); } public static bool RegisterHook () { lock ( Lock ) { if ( IsRegistered ) return true ; Delegate = LowLevelKeyboardHandler ; Hook = SetWindowsHookEx ( WH_KEYBOARD_LL , Delegate , Marshal . GetHINSTANCE ( System . Reflection . Assembly . GetExecutingAssembly (). GetModules ()[ ] ). ToInt32 (), ); if ( Hook != ) return IsRegistered = true ; Delegat = null ; returnera falskt ; } } public static bool UnregisterHook () { lock ( Lock ) { return IsRegistered = ( UnhookWindowsHookEx ( Hook ) != ); } } }
API/funktionskoppling/avlyssning med JMP-instruktion aka splitsning
Följande källkod är ett exempel på en API/funktions hooking-metod som hookar genom att skriva över de första sex byten av en destinationsfunktion med en JMP -instruktion till en ny funktion. Koden kompileras till en DLL- fil och laddas sedan in i målprocessen med valfri DLL-injektionsmetod . Genom att använda en säkerhetskopia av den ursprungliga funktionen kan man sedan återställa de första sex byten igen så att samtalet inte avbryts. I det här exemplet win32 API -funktionen MessageBoxW ansluten.
0
0
/* Den här idén är baserad på chrom-lib-metoden, distribuerad under GNU LGPL-licens. Källa chrom-lib: https://github.com/linuxexp/chrom-lib Copyright (C) 2011 Raja Jamwal */ #include <windows.h> #define SIZE 6 typedef int ( WINAPI * pMessageBoxW )( HWND , LPCWSTR , LPCWSTR , UINT ); // Messagebox prototyp int WINAPI MyMessageBoxW ( HWND , LPCWSTR , LPCWSTR , UINT ); // Vår omväg void BeginRedirect ( LPVOID ); pMessageBoxW pOrigMBAddress = NULL ; // adress för original BYTE oldBytes [ SIZE ] = { }; // backup BYTE JMP [ SIZE ] = { }; // 6 byte JMP-instruktion DWORD oldProtect , myProtect = PAGE_EXECUTE_READWRITE ; INT APIENTRY DllMain ( HMODULE hDLL , DWORD Reason , LPVOID Reserved ) { switch ( Reason ) { case DLL_PROCESS_ATTACH : // if added pOrigMBAddress = ( pMessageBoxW ) GetProcAddress ( GetModuleHandleA ( getModuleHandleA " ( getModuleHandleA" ) "user32Me/sage " " ); if ( pOrigMBAddress != NULL ) BeginRedirect ( MyMessageBoxW ); // starta omvägspaus ; case DLL_PROCESS_DETACH : VirtualProtect (( LPVOID ) pOrigMBAddress , SIZE , myProtect och oldProtect ) ; // tilldela läs-skrivskydd memcpy ( pOrigMBAddress , oldBytes , SIZE ); // återställa backup VirtualProtect (( LPVOID ) pOrigMBAddress , SIZE , oldProtect , & myProtect ); // återställ skyddsfall DLL_THREAD_ATTACH : fall DLL_THREAD_DETACH : break ; } returnera TRUE ; } void BeginRedirect ( LPVOID newFunction ) { BYTE tempJMP [ STORLEK ] = { 0xE9 , 0x90 , 0x90 , 0x90 , 0x90 , 0xC3 }; // 0xE9 = JMP 0x90 = NOP 0xC3 = RET memcpy ( JMP , tempJMP , SIZE ); // lagra jmp -instruktion till JMP DWORD JMPSize = (( DWORD ) newFunction- ( DWORD ) pOrigMBAddress - 5 ); // beräkna hoppavstånd VirtualProtect (( LPVOID ) pOrigMBAddress , SIZE , // tilldela läs-skrivskydd PAGE_EXECUTE_READWRITE , & oldProtect ); memcpy ( oldBytes , pOrigMBAddress , SIZE ); // gör backup memcpy ( & JMP [ 1 ], & JMPSize , 4 ); // fyll nops med hoppavståndet (JMP,distance(4bytes),RET) memcpy ( pOrigMBAddress , JMP , SIZE ); // ställ in hoppinstruktioner i början av den ursprungliga funktionen VirtualProtect (( LPVOID ) pOrigMBAddress , SIZE , oldProtect , & myProtect ); // återställ skydd } int WINAPI MyMessageBoxW ( HWND hWnd , LPCWSTR lpText , LPCWSTR lpCaption , UINT uiType ) { VirtualProtect (( LPVOID ) pOrigMBAddress , SIZE , myProtect , & oldProtect ); // tilldela läs-skrivskydd memcpy ( pOrigMBAddress , oldBytes , SIZE ); // restore backup int retValue = MessageBoxW ( hWnd , lpText , lpCaption , uiType ); // få returvärdet för den ursprungliga funktionen memcpy ( pOrigMBAddress , JMP , SIZE ); // ställ in hoppinstruktionen igen VirtualProtect (( LPVOID ) pOrigMBAddress , SIZE , oldProtect , & myProtect ); // återställ skydd return retValue ; // returnera ursprungligt returvärde }
Nätfilterkrok
Det här exemplet visar hur man använder hooking för att ändra nätverkstrafik i Linux-kärnan med hjälp av Netfilter .
#inkludera <linux/modul.h> #inkludera <linux/kernel.h> #inkludera <linux/skbuff.h> #inkludera <linux/ip.h> #inkludera <linux/tcp.h> #inkludera <linux/ in.h> #include <linux/netfilter.h> #include <linux/netfilter_ipv4.h> /* Port vi vill släppa paket på */ static const uint16_t port = 25 ; /* Detta är själva hook-funktionen */ static unsigned int hook_func ( unsigned int hooknum , struct sk_buff ** pskb , const struct net_device * in , const struct net_device * out , int ( * okfn )( struct sk_buff * )) { struct iphdr * iph = ip_hdr ( * pskb ); struct tcphdr * tcph , tcpbuf ; if ( iph -> protokoll != IPPROTO_TCP ) returnera NF_ACCEPT ; tcph = skb_header_pointer ( * pskb , ip_hdrlen ( * pskb ), sizeof ( * tcph ), & tcpbuf ); if ( tcph == NULL ) returnera NF_ACCEPT ; returnera ( tcph -> dest == port ) ? NF_DROP : NF_ACCEPT ; } /* Används för att registrera vår hook-funktion */ static struct nf_hook_ops nfho = { . hook = hook_func , . hooknum = NF_IP_PRE_ROUTING , . pf = NFPROTO_IPV4 , . prioritet = NF_IP_PRI_FIRST , }; static __init int my_init ( void ) { return nf_register_hook ( & nfho ); } static __exit void my_exit ( void ) { nf_unregister_hook ( & nfho ); } module_init ( my_init ); module_exit ( my_exit );
Intern IAT-krokning
Följande kod visar hur man kopplar funktioner som importeras från en annan modul. Detta kan användas för att koppla funktioner i en annan process än anropsprocessen. För detta måste koden kompileras till en DLL- fil och sedan laddas in i målprocessen med någon metod för DLL-injektion . Fördelen med denna metod är att den är mindre upptäckbar av antivirusprogram och/eller anti-fuskprogramvara, man kan göra detta till en extern hook som inte använder några skadliga samtal. Rubriken Portable Executable innehåller Import Address Table (IAT), som kan manipuleras enligt källan nedan. Källan nedan körs under Microsoft Windows.
0
0
0
0
0
0
0
0
#include <windows.h> typedef int ( __stdcall * pMessageBoxA ) ( HWND hWnd , LPCSTR lpText , LPCSTR lpCaption , UINT uType ); //Detta är 'typen' för MessageBoxA-anropet. pMessageBoxA RealMessageBoxA ; //Detta lagrar en pekare till den ursprungliga funktionen. void DetourIATptr ( const char * function , void * newfunction , HMODULE- modul ); int __stdcall NewMessageBoxA ( HWND hWnd , LPCSTR lpText , LPCSTR lpCaption , UINT uType ) { //Vår falska funktion printf ( "Strängen som skickades till MessageBoxA Was : %s \n " , lpText ); returnera RealMessageBoxA ( hWnd , lpText , lpCaption , uType ); //Call the real function } int main ( int argc , CHAR * argv []) { DetourIATptr ( "MessageBoxA" ,( void * ) NewMessageBoxA , ); //Hook funktionen MessageBoxA ( NULL , "Just A MessageBox" , "Just A MessageBox" , ); //Anropa funktionen -- detta kommer att anropa vår falska hook. återvända ; } void ** IATfind ( const char * function , HMODULE module ) { //Hitta IAT-posten (Import Address Table) som är specifik för den givna funktionen. int ip = ; if ( modul == ) module = GetModuleHandle ( ); PIMAGE_DOS_HEADER pImgDosHeaders = ( PIMAGE_DOS_HEADER ) modul ; PIMAGE_NT_HEADERS pImgNTHeaders = ( PIMAGE_NT_HEADERS )(( LPBYTE ) pImgDosHeaders + pImgDosHeaders -> e_lfanew ); PIMAGE_IMPORT_DESCRIPTOR pImgImportDesc = ( PIMAGE_IMPORT_DESCRIPTOR )(( LPBYTE ) pImgDosHeaders + pImgNTHeaders - > OptionalHeader . DataDirectory [ IMAGE_DIRECTORY_ENTRY_IMPORT ] . if ( pImgDosHeaders -> e_magic != IMAGE_DOS_SIGNATURE ) printf ( "libPE Fel: e_magic är ingen giltig DOS-signatur \n " ); för ( IMAGE_IMPORT_DESCRIPTOR * iid = pImgImportDesc ; iid -> Name != NULL ; iid ++ ) { for ( int funcIdx = ; * ( funcIdx + ( LPVOID * )( iid -> FirstThunk + ( STORLEK ) _T ! ) modul ; funcIdx ++ ) { char * modFuncName = ( char * )( * ( funcIdx + ( SIZE_T * ) ( iid -> OriginalFirstThunk + ( SIZE_T ) modul )) + ( SIZE_T ) modul + 2 ); const uintptr_t nModFuncName = ( uintptr_t ) modFuncName ; bool isString = ! ( nModFuncName & ( sizeof ( nModFuncName ) == 4 ? 0x80000000 : 0x80000000000000000 )); if ( isString ) { if ( ! _stricmp ( funktion , modFuncName )) returnerar funcIdx + ( LPVOID * ) ( iid -> FirstThunk + ( SIZE_T ) modul ); } } } returnera ; } void DetourIATptr ( const char * function , void * newfunction , HMODULE modul ) { void ** funcptr = IATfind ( funktion , modul ); if ( * funcptr == nyfunktion ) returnera ; DWORD oldrights , newrights = PAGE_READWRITE ; //Uppdatera skyddet till READWRITE VirtualProtect ( funcptr , sizeof ( LPVOID ), newrights , & oldrights ); RealMessageBoxA = ( pMessageBoxA ) * funcptr ; //Vissa kompilatorer kräver cast som "MinGW" osäker på MSVC * funcptr = newfunction ; //Återställ de gamla minnesskyddsflaggorna. VirtualProtect ( funcptr , sizeof ( LPVOID ), oldrights & newrights ) ; }
Se även
- Callback (datavetenskap)
- Delegering (programmering)
- Avsluta-och-bo-bo-program
- Användaravsluta
- WinAPIOverride32
- Jonathan Daniel (2013-11-27). "Hooking förklaras: omvägar biblioteksanrop och vtable patching i Windows/Linux/MAC-OSX" . Hämtad 2014-01-01 .
-
Binh Nguyen (2004-08-16). "Hacking-Lexicon / Linux Dictionary V 0.16" . Hämtad 2008-02-23 .
Krok
-
[2012-06-29: Länk verkar vara död] Författare: Helige Fader (2002-06-10). "Hooking Windows API - Teknik för att koppla API-funktioner på Windows 1.1 engelska" ( PDF) . Arkiverad från originalet (PDF) 2009-12-29 . Hämtad 2008-02-21 .
{{ citera webben }}
:|author=
har ett generiskt namn ( hjälp )
externa länkar
Windows
- Information om importadresstabellfunktionskoppling.
- Information från Microsoft om hooking
- Information och olika tekniker gällande x86-hakning.
- APISpy32 är en applikation som används för att ansluta win32 API.
- Detours är ett allmänt funktionshokningsbibliotek skapat av Microsoft Research som fungerar i C/C++.
- winspy Tre sätt att injicera kod i en annan process.
- HookTool SDK (ACF SDK) Ger en omfattande översikt över API-hooking och kodinjektion. En kommersiell produkt tillgänglig också.
- madCodeHook är ett kommersiellt x86 och x64 API-hooking- och DLL-injektionsbibliotek för C++ och Delphi.
- EasyHook är en hooking-motor med öppen källkod som stöder x86 och x64 i Windows i både användar- och kärnland.
- SpyStudio Application Trace SpyStudio är en Application Trace som kopplar samtal och visar resultaten på ett strukturerat sätt.
- rohitab.com API Monitor är ett gratisprogram som kan koppla och visa 10 000+ Windows API:er och COM-gränssnitt i 32-bitars och 64-bitars applikationer och tjänster.
- Deviare API Hook Deviare är ett freeware inter-process hook-ramverk som kan användas för att fånga upp andra processers API-anrop och visa fullständig parameterinformation eller skapa API-monitorer.
- WinAPIOverride WinAPIOverride är ett gratisprogram för icke-kommersiellt bruk. Den kan koppla win32 API, COM, OLE, ActiveX, .NET i 32-bitars och 64-bitars processer. Det inkluderar övervakning efter analysverktyg.
- urmem C++11 plattformsoberoende bibliotek (x86) för att arbeta med minne (krokar, patchar, pekares omslag, signaturskanner etc.)
Linux
- [2] Ett studentforskningsprojekt som använder hooking.
- [3] Funktionalitet som gör att en mjukvara kan observera och kontrollera exekveringen av en annan process.
- [4] Användning av LD_PRELOAD för att koppla delade bibliotekssamtal.
Emacs
- Emacs Hooks Hooks är en viktig mekanism för anpassning av Emacs. En hook är en Lisp-variabel som innehåller en lista med funktioner som ska anropas vid något väldefinierat tillfälle. (Detta kallas att köra kroken.)
OS X och iOS
- Cydia Substrate är ett ramverk för jailbroken iOS-enheter som gör det möjligt för utvecklare att ansluta sig till alla andra ramverk eller applikationer.
- harpoon är ett OS X-bibliotek för körtidsfunktionskoppling.
Fördjupad API Hooking
- x86 API Hooking Avmystifierad artikel om olika API Hooking-metoder, för x86-arkitekturen.