Page 1 sur 1

API Hooking

Publié : ven. 13/nov./2009 21:40
par nico
Voici un exemple de Hook d'API

Je ne peux malheureusement pas vous en expliquer le fonctionnement car c'est un code que j'ai récupérer sur le net; il n'est pas long, encore faut il le comprendre.

[Edit]
Je pense que Denis pourrait nous expliquer plus clairement ce qui se passe
Ce que j'ai compris, c'est qu'à l'adresse de la fonction MessageBox, on écrit en assembleur un saut qui
correspond à jmp(0)=$e9, ensuite on copie l'adresse du saut sur les Bytes suivant:
jmp(1)=$00,jmp(2)=$00,jmp(3)=$00,jmp(4)=$00
par contre, je ne sais pas à quoi correspond jmp(5)=$c3
[/Edit]

J'ai modifier le code d'origine pour qu'il puisse fonctionner en Unicode

Le programme à tester va hooker l'API MessageBoxA (si vous compilez en Ascii)
ou l'API MessageBoxW (si vous compilez en Unicode)
Le programme se compose d'une fenêtre avec un bouton, un clic sur le bouton affiche un MessageRequester -->"MessageRequester("Information","PureBasic")"
Si le Hook réussi, le texte suivant "(MessageBox Hooked)" sera ajouté à "PureBasic"

Voici le code:

Code : Tout sélectionner

; Exemple de Hook d'API
; On va intercepter l'API MessageBoxA ou MessageBoxW suivant qu'on compile en unicode ou pas
; ainsi on pourra modifier le message affiché

Global Dim hook.b(6)
Global *memoire
Global Library.s

Procedure.l HookFunction( *lpModule.l, *lpFuncName, *lpFunction, *lpBackup)

  ModuleHandle=GetModuleHandle_(*lpModule)
  Debug ModuleHandle
  
  
  dwAddr.l=GetProcAddress_(ModuleHandle,*lpFuncName)
  Debug dwAddr
  
  Dim jmp.b(5)
  jmp(0)=$e9 ;//jmp
  ;-------------------------
  jmp(1)=$00 ;//address
  jmp(2)=$00
  jmp(3)=$00
  jmp(4)=$00
  jmp(5)=$c3
  
  ReadProcessMemory_(GetCurrentProcess_(), dwAddr,*lpBackup, 6, 0)
  
  dwCalc.l=(*lpFunction-dwAddr-5);	//((to)-(from)-5)
  
  CopyMemory(@dwCalc, @jmp(1), 4);	//build the jmp
  
  WriteProcessMemory_(GetCurrentProcess_(), dwAddr, @jmp(), 6, 0)
  
  ProcedureReturn dwAddr
EndProcedure


Procedure.l UnHookFunction(*lpModule,*lpFuncName,*lpBackup)
  
  dwAddr.l=GetProcAddress_(GetModuleHandle_(*lpModule),*lpFuncName);
  
  If (WriteProcessMemory_(GetCurrentProcess_(), dwAddr,*lpBackup, 6, 0))
    ProcedureReturn #True
  Else
    ProcedureReturn #False
  EndIf
EndProcedure

Procedure.l MyMessageBox(hWnd.l,*lpText,*lpCaption, uType.l)
  
  UnHookFunction(@Library,*memoire, @hook())
  
  Message.s=PeekS(*lpText)+Chr(13)+"(MessageBox Hooked)"
  x.l=MessageBox_(hWnd, Message, PeekS(*lpCaption), uType)
  
  HookFunction(@Library,*memoire, @MyMessageBox(), @hook())
  ProcedureReturn x
EndProcedure


Library="User32.dll"
CompilerIf #PB_Compiler_Unicode
  API.s="MessageBoxW"
CompilerElse
  API.s="MessageBoxA"
CompilerEndIf

Longueur=MemoryStringLength(@API) 
*memoire=AllocateMemory(Longueur+1)
PokeS(*memoire, API,-1,#PB_Ascii)

HookFunction(@Library, *memoire, @MyMessageBox(), @hook())

If OpenWindow(0, 0, 0, 222, 200, "ButtonGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) 
  ButtonGadget(0, 10,  10, 200, 20, "Bouton standard")
  Repeat
  event=WaitWindowEvent()
  Select event
    Case #PB_Event_Gadget
      Select EventGadget()
        Case 0
          MessageRequester("Information","PureBasic")
      EndSelect
      
  EndSelect
      
  Until event= #PB_Event_CloseWindow
EndIf

Re: API Hooking

Publié : ven. 13/nov./2009 21:58
par nico
Bon c'est super, mais ça n'a aucun intérêt de Hooker les API de ses propres programmes, alors voici un exemple de Hooking sur le Bloc-notes (Tester sur Window XP)

Tout d'abord, compiler cette Dll en unicode:

Code : Tout sélectionner

Global Dim hook.b(6)
Global *memoire
Global Library.s

Procedure.l HookFunction( *lpModule, *lpFuncName, *lpFunction, *lpBackup)
  
  dwAddr.l=GetProcAddress_(GetModuleHandle_(*lpModule), *lpFuncName)
  
  Dim jmp.b(5)
  jmp(0)=$e9 ;//jmp
  ;-------------------------
  jmp(1)=$00 ;//address
  jmp(2)=$00
  jmp(3)=$00
  jmp(4)=$00
  jmp(5)=$c3
  
  ReadProcessMemory_(GetCurrentProcess_(), dwAddr, *lpBackup, 6, 0)
  
  dwCalc.l=(*lpFunction-dwAddr-5);	//((to)-(from)-5)
  
  CopyMemory(@dwCalc,@jmp(1), 4);	//build the jmp
  
  WriteProcessMemory_(GetCurrentProcess_(), dwAddr, @jmp(), 6, 0)
  
  ProcedureReturn dwAddr
EndProcedure


Procedure.l UnHookFunction( *lpModule, *lpFuncName, *lpBackup)
  
  dwAddr.l=GetProcAddress_(GetModuleHandle_(*lpModule),*lpFuncName);
  
  If (WriteProcessMemory_(GetCurrentProcess_(), dwAddr, *lpBackup, 6, 0))
    ProcedureReturn #True
  Else
    ProcedureReturn #False
  EndIf
EndProcedure

Procedure.l MyMessageBox( hWnd.l, *lpText, *lpCaption, uType.l)
  
  UnHookFunction( @Library, *memoire, @hook())
  
  Message.s=PeekS(*lpText)+Chr(13)+"(MessageBox Hooked)"
  x.l=MessageBox_(hWnd, Message, PeekS(*lpCaption), uType)
  
  HookFunction ( @Library, *memoire, @MyMessageBox(), @hook())
  ProcedureReturn x
EndProcedure

; This procedure is called once, when the program loads the library
; for the first time. All init stuffs can be done here (but not DirectX init)
ProcedureDLL AttachProcess(Instance)
  API.s="MessageBoxW"
  Library="user32.dll"
  
  Longueur=MemoryStringLength(@API) 
  *memoire=AllocateMemory(Longueur+1)
  PokeS(*memoire, API,-1,#PB_Ascii)
  
  HookFunction(@Library, *memoire, @MyMessageBox(), @hook())
EndProcedure

; Called when the program release (free) the DLL
;
ProcedureDLL DetachProcess(Instance)
EndProcedure

; Both are called when a thread in a program call or release (free) the DLL
;
ProcedureDLL AttachThread(Instance)
EndProcedure

ProcedureDLL DetachThread(Instance)
EndProcedure
Ensuite lancer le Bloc-notes
Puis exécuter le code suivant qui permet d'injecter une Dll dans un exécutable
Il va vous demander le Target Process Name, écrire Notepad.exe
ensuite, il va falloir lui indiquer la dll à injecter, indiquer celle que vous avez créer précédemment
Taper n'importe quoi dans le bloc-notes, cliquer sur la croix de fermeture ou faites menu--> fichier--> quitter
et là, le message (MessageBox Hooked) apparaît en plus dans le message du Bloc-notes

Le code qui permet d'injecter une Dll dans un exécutable:

Code : Tout sélectionner

; Code de SFSxOI
; Injecter une Dll dans un exécutable

Prototype.i PFNCreateToolhelp32Snapshot(dwFlags.i, th32ProcessID.i) ;
Prototype.b PFNProcess32First(hSnapshot.i, *lppe.PROCESSENTRY32) ;
Prototype.b PFNProcess32Next(hSnapshot.i, *lppe.PROCESSENTRY32) ;

Procedure GetPidByName(p_name$)
    Protected hDLL.i, process_name$
    Protected PEntry.PROCESSENTRY32, hTool32.i
    Protected pCreateToolhelp32Snapshot.PFNCreateToolhelp32Snapshot
    Protected pProcess32First.PFNProcess32First
    Protected pProcess32Next.PFNProcess32Next
    Protected pid.i
   
    hDLL = OpenLibrary(#PB_Any,"kernel32.dll")
   
    If hDLL
        pCreateToolhelp32Snapshot = GetFunction(hDLL,"CreateToolhelp32Snapshot")
        pProcess32First = GetFunction(hDLL,"Process32First")
        pProcess32Next = GetFunction(hDLL,"Process32Next")
    Else
        ProcedureReturn 0
    EndIf
   
    PEntry\dwSize = SizeOf(PROCESSENTRY32)
    hTool32 = pCreateToolhelp32Snapshot(#TH32CS_SNAPPROCESS, 0)
    pProcess32First(hTool32, @PEntry)
    process_name$ = Space(#MAX_PATH)
    CopyMemory(@PEntry\szExeFile,@process_name$,#MAX_PATH)
   
    If  UCase(process_name$) = UCase(p_name$)
        ProcedureReturn PEntry\th32ProcessID
    EndIf
   
    While pProcess32Next(hTool32, @PEntry) > 0
        process_name$ = Space(#MAX_PATH)
        CopyMemory(@PEntry\szExeFile,@process_name$,#MAX_PATH)
       
        If  UCase(process_name$) = UCase(p_name$)
            ProcedureReturn PEntry\th32ProcessID
        EndIf
   
    Wend
   
    CloseLibrary(hDLL)
   
    ProcedureReturn 0
EndProcedure

Procedure InjectLibA(dwProcessId.i, pszLibFile$)
  hProcess.i
  hThread.i
  lzLibFileRemote.i
  lSize.i
  endSize.i
  lsThreadRtn.i
 
  hProcess = OpenProcess_(#PROCESS_QUERY_INFORMATION | #PROCESS_CREATE_THREAD | #PROCESS_VM_OPERATION | #PROCESS_VM_WRITE, 0, dwProcessId)
 
  If hProcess = 0 : Goto ErrHandle : EndIf
  lSize = 1 + Len(pszLibFile$)
  endSize = lSize
 
  lzLibFileRemote = VirtualAllocEx_(hProcess, #Null, endSize, #MEM_COMMIT, #PAGE_READWRITE)
 
  If lzLibFileRemote = 0 : Goto ErrHandle : EndIf
 
  If (WriteProcessMemory_(hProcess, lzLibFileRemote, pszLibFile$, endSize, #Null) = 0) : Goto ErrHandle : EndIf
 
  OpenLibrary(0, "Kernel32.dll") : lsThreadRtn = GetFunction(0, "LoadLibraryA") : CloseLibrary(0)
 
  If lsThreadRtn = 0 : Goto ErrHandle : EndIf
 
  hThread = CreateRemoteThread_(hProcess, #Null, #Null, lsThreadRtn, lzLibFileRemote, #Null, #Null)
 
  If (hThread = 0) : Goto ErrHandle : EndIf
 
  WaitForSingleObject_(hThread, #INFINITE)
 
  If lzLibFileRemote<>0
    VirtualFreeEx_(hProcess, lzLibFileRemote, 0, #MEM_RELEASE)
    MessageRequester("Inject Status", "Injection Suceeded", 0)
    Else
    VirtualFreeEx_(hProcess, lzLibFileRemote, 0, #MEM_RELEASE)
    MessageRequester("Inject Status", "Injection Failed !!!", 0)
  EndIf
  End
 
  ErrHandle:
      CloseHandle_(hThread)
      CloseHandle_(hProcess)
EndProcedure

Input_proc$ = InputRequester("Simple DLL injector", "Please enter target process name (.exe):", "") ;enter process name i.e...notepad.exe
val_pid.i = GetPidByName(Input_proc$)
Delay(10)
File_dll$ = OpenFileRequester("Choose .dll file to inject", "C:\", "DLL File (*.dll)|*.dll;*.dll", 0)
Delay(10)
InjectLibA(val_pid, File_dll$)

Re: API Hooking

Publié : sam. 14/nov./2009 0:13
par Jacobus
Je n'ai pas le temps de tester ton code mais ça m'intéresse cette possibilité.
A voir comment se comporte les protections sur Vista ou Seven...

Re: API Hooking

Publié : lun. 16/nov./2009 12:29
par Cls
De mémoire, les AV n'apprécient guère l'injection de DLL. A voir si Vista ou Seven sont aussi tatillons.