Seite 1 von 2

DLL-Injection ohne Remote Thread

Verfasst: 22.05.2007 23:39
von Thorium
Da es doch einige Programme (vorallem Spiele) gibt, die es erkennen wenn ein Thread per CreateRemoteThread erstellt wird, habe ich eine etwas kompliziertere Technik zur DLL-Injection in PB umgesetzt. Bei dieser Technik wird kein Thread erzeugt, sondern die Ausführung eines Threads des Zielprozesses auf eigenen Code umgebogen, der dann die DLL läd.

Wenn ihr das testen wollt mit einer DLL, welche eine MessageBox ausgibt, dann vergesst nicht folgende Zeile am Ende auszukommentieren:

Code: Alles auswählen

VirtualFreeEx_(hTarget,CodeAddr,0,#MEM_RELEASE)
Da der Zielprozess sonst nach dem schließen der MessageBox crasht, da der nachfolgende Code nichtmehr existiert.

Wenn die Injektion geklappt hat gibt die Prozedur 1 zurück andernfalls 0.

Code: Alles auswählen

;Written by Thorium
;PureBasic 4.02 Code

EnableExplicit

#THREAD_ALL_ACCESS = $1F03FF

Procedure.l InjectDLL(idTarget.l,FileName.s)
  Define.l FileNameLen,FileNameAddr,CodeAddr,BytesWritten,hKernel32,LoadLibraryAAddr,CodeBuffer,Position,idThread,hThread,hSnapshot,RetVal,LibKernel32,hTarget
  Define.CONTEXT ThreadContext
  Define.THREADENTRY32 ThreadInfo
  
  ;Zielprozess öffnen
  hTarget = OpenProcess_(#PROCESS_ALL_ACCESS,0,IdTarget)
  If hTarget = 0
    ProcedureReturn 0
  EndIf

  ;ID des Hauptthreads ermitteln
  ThreadInfo\dwSize = SizeOf(ThreadInfo)
  hSnapshot = CreateToolhelp32Snapshot_(#TH32CS_SNAPTHREAD,0)
  If hSnapshot <> 0
    RetVal = Thread32First_(hSnapshot,@ThreadInfo)
    If RetVal <> 0
      If ThreadInfo\th32OwnerProcessID = idTarget
        idThread = ThreadInfo\th32ThreadID
      Else
        Repeat
          RetVal = Thread32Next_(hSnapshot,@ThreadInfo)
          If RetVal = 0
            Break
          Else
            If ThreadInfo\th32OwnerProcessID = idTarget
              idThread = ThreadInfo\th32ThreadID
              Break
            EndIf
          EndIf
        Until RetVal = 0
      EndIf
      CloseHandle_(hSnapshot)
    EndIf
  EndIf
  If idThread = 0
    CloseHandle_(hTarget)
    ProcedureReturn 0
  EndIf
  
  ;Addresse von LoadLibraryA ermitteln
  hKernel32 = GetModuleHandle_("Kernel32.dll")
  If hKernel32 = 0
    CloseHandle_(hTarget)
    ProcedureReturn 0
  EndIf
  LoadLibraryAAddr = GetProcAddress_(hKernel32,"LoadLibraryA")
  If LoadLibraryAAddr = 0
    CloseHandle_(hTarget)
    ProcedureReturn 0
  EndIf

  ;Thread öffnen
  LibKernel32 = OpenLibrary(#PB_Any,"Kernel32.dll")
  hThread = CallFunction(LibKernel32,"OpenThread",#THREAD_ALL_ACCESS,0,idThread)
  If hThread = 0
    CloseHandle_(hTarget)
    ProcedureReturn 0
  EndIf
  
  ;Dateiname der DLL in den Zielprozess schreiben
  FileNameLen = Len(FileName) + 1
  FileNameAddr = VirtualAllocEx_(hTarget,0,FileNameLen,#MEM_COMMIT | #MEM_RESERVE,#PAGE_READWRITE)
  If FileNameAddr = 0
    CloseHandle_(hThread)
    CloseHandle_(hTarget)
    ProcedureReturn 0
  EndIf
  If WriteProcessMemory_(hTarget,FileNameAddr,FileName,FileNameLen,@BytesWritten) = 0
    CloseHandle_(hThread)
    CloseHandle_(hTarget)
    ProcedureReturn 0
  EndIf
  If BytesWritten <> FileNameLen
    CloseHandle_(hThread)
    CloseHandle_(hTarget)
    ProcedureReturn 0
  EndIf
  
  ;Speicher im Zielprozess für den Code allokieren
  CodeAddr = VirtualAllocEx_(hTarget,0,22,#MEM_COMMIT | #MEM_RESERVE,#PAGE_EXECUTE_READWRITE)
  If CodeAddr = 0
    CloseHandle_(hThread)
    VirtualFreeEx_(hTarget,FileNameAddr,0,#MEM_RELEASE)
    CloseHandle_(hTarget)
    ProcedureReturn 0
  EndIf
  
  ;Speicher für die Codegenerierung allokieren
  CodeBuffer = AllocateMemory(22)
  
  ;Hauptthread anhalten
  If SuspendThread_(hThread) = -1
    CloseHandle_(hThread)
    VirtualFreeEx_(hTarget,FileNameAddr,0,#MEM_RELEASE)
    VirtualFreeEx_(hTarget,CodeAddr,0,#MEM_RELEASE)
    FreeMemory(CodeBuffer)
    CloseHandle_(hTarget)
    ProcedureReturn 0
  EndIf
  
  ;EIP-Register des Hauptthreads besorgen
  ThreadContext\ContextFlags = #CONTEXT_CONTROL
  If GetThreadContext_(hThread,@ThreadContext) = 0
    VirtualFreeEx_(hTarget,FileNameAddr,0,#MEM_RELEASE)
    VirtualFreeEx_(hTarget,CodeAddr,0,#MEM_RELEASE)
    FreeMemory(CodeBuffer)
    ResumeThread_(hThread)
    CloseHandle_(hThread)
    CloseHandle_(hTarget)
    ProcedureReturn 0
  EndIf
  
  ;Code generieren
  PokeB(CodeBuffer,$68) ;push
  Position + 1
  PokeL(CodeBuffer + Position,ThreadContext\Eip)
  Position + 4
  PokeB(CodeBuffer + Position,$9C) ;pushfd
  Position + 1
  PokeB(CodeBuffer + Position,$60) ;pushad
  Position + 1
  PokeB(CodeBuffer + Position,$68) ;push
  Position + 1
  PokeL(CodeBuffer + Position,FileNameAddr)
  Position + 4
  PokeB(CodeBuffer + Position,$B8) ;mov eax,const
  Position + 1
  PokeL(CodeBuffer + Position,LoadLibraryAAddr)
  Position + 4
  PokeW(CodeBuffer + Position,$D0FF) ;call eax
  Position + 2
  PokeB(CodeBuffer + Position,$61) ;popad
  Position + 1
  PokeB(CodeBuffer + Position,$9D) ;popfd
  Position + 1
  PokeB(CodeBuffer + Position,$C3) ;ret
  
  ;Code in den Zielprozess schreiben
  If WriteProcessMemory_(hTarget,CodeAddr,CodeBuffer,22,@BytesWritten) = 0
    VirtualFreeEx_(hTarget,FileNameAddr,0,#MEM_RELEASE)
    VirtualFreeEx_(hTarget,CodeAddr,0,#MEM_RELEASE)
    FreeMemory(CodeBuffer)
    ResumeThread_(hThread)
    CloseHandle_(hThread)
    CloseHandle_(hTarget)
    ProcedureReturn 0
  EndIf
  If BytesWritten <> 22
    VirtualFreeEx_(hTarget,FileNameAddr,0,#MEM_RELEASE)
    VirtualFreeEx_(hTarget,CodeAddr,0,#MEM_RELEASE)
    FreeMemory(CodeBuffer)
    ResumeThread_(hThread)
    CloseHandle_(hThread)
    CloseHandle_(hTarget)
    ProcedureReturn 0
  EndIf
  
  ;EIP-Register auf den Code setzen
  ThreadContext\Eip = CodeAddr
  ThreadContext\ContextFlags = #CONTEXT_CONTROL
  SetThreadContext_(hThread,@ThreadContext)

  ;Zielprozess weiterlaufen lassen
  ResumeThread_(hThread)
  
  ;Aufräumen
  Delay(1000)
  VirtualFreeEx_(hTarget,FileNameAddr,0,#MEM_RELEASE)
  VirtualFreeEx_(hTarget,CodeAddr,0,#MEM_RELEASE)
  FreeMemory(CodeBuffer)
  CloseHandle_(hThread)
  CloseHandle_(hTarget)
  
  ProcedureReturn 1
EndProcedure

Verfasst: 23.05.2007 10:35
von Rings
hättest vielleicht ein paar Namen 'klarer' machen können:

Procedure.l InjectDLL(ProcessID,DLLFileName.s)

Verfasst: 23.05.2007 12:37
von dige
Ein Beispiel wie man das anwendet wäre auch nicht schlecht .. Danke! :D

Verfasst: 23.05.2007 14:54
von Thorium
dige hat geschrieben:Ein Beispiel wie man das anwendet wäre auch nicht schlecht .. Danke! :D
Hm, naja Beispiel.

Also idTarget ist PID also die ID des Zielprozesses.
FileName ist der Name der DLL, welche injiziert werden soll. Kann auch einen Pfad enthalten.
Stimmt Namen sind ein bissel blöd gewählt. :oops:

So richtig ein nützliches einfaches Beispiel kann ich hier garnicht bringen. Wenn du DLL-Injection brauchst, dann sollte das Aufrufen der Prozedur auch ohne Beispiel kein Problem darstellen. Wenn du nicht weisst wofür du DLL-Injection brauchen könntest, dann brauchst du es warscheinlich auch nicht. :wink:

Du kannst damit halt eine DLL in einen fremden Prozess einschleusen um deinen eigenen Code dort laufen zu lassen.

Verfasst: 23.05.2007 18:37
von edel
Waere es nicht besser das Modulehandle zurueck zu geben,
bzw ist es so ueberhaupt moeglich die DLL wieder zu entladen ?

Verfasst: 23.05.2007 19:21
von Thorium
edel hat geschrieben:Waere es nicht besser das Modulehandle zurueck zu geben,
bzw ist es so ueberhaupt moeglich die DLL wieder zu entladen ?
Naja gute Frage, das Handle befindet sich ja in dem Zielprozess. Man müsste mal schauen ob das ModuleHandle überhaupt übertragbar ist. Dann könnte man das Handle an den aufrufenden Prozess zurückgeben. Also so genau weis ich das jetzt nicht, müsste ich mich mal schlau machen. Ansonsten währe es natürlich interessant wenn sich die DLL selbst entladen könnte.

Edit: Dabei fällt mir ein, das Handle muss ja garnicht übertragbar sein. Man kann einfach Code in den Zielprozess injizieren, welcher das Handle nutzt um dort die DLL zu entladen. Das ist eine gute Idee, werde das demnächst mal umsetzen. :mrgreen:

Verfasst: 23.05.2007 19:28
von edel
Wie sieht es denn mit GetExitCodeThread aus, aber der Thread wird ja hier nicht beendet oder ?

Verfasst: 23.05.2007 20:54
von Thorium
edel hat geschrieben:Wie sieht es denn mit GetExitCodeThread aus, aber der Thread wird ja hier nicht beendet oder ?
Naja der Thread ist ja hier ein bestehender Thread des Zielprozesses, dessen Ausführung nur kurzzeitig auf ein wenig eigenen Code umgeleitet wird. Der Thread läuft nach dem Laden der DLL so weiter wie vom Zielprozess beabsichtigt.

So oder so entläd das Beenden des Threads aber nicht die DLL.

Verfasst: 25.05.2007 08:40
von edel
Thorium hat geschrieben:So oder so entläd das Beenden des Threads aber nicht die DLL.
Nein , aber wenn man das Handle zur DLL kennt, koennte man es
ja (ueber den gleichen Weg) wieder mit FreeLibrary freigeben :?

Verfasst: 25.05.2007 19:40
von Thorium
edel hat geschrieben:
Thorium hat geschrieben:So oder so entläd das Beenden des Threads aber nicht die DLL.
Nein , aber wenn man das Handle zur DLL kennt, koennte man es
ja (ueber den gleichen Weg) wieder mit FreeLibrary freigeben :?
Jep, hab ich ja auch geschrieben.
Siehe das Edit in meinem vorletzten Post. ^^