Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi x64 main-thread hook mittels dll (https://www.delphipraxis.net/189082-x64-main-thread-hook-mittels-dll.html)

Clowdy 1. Mai 2016 18:49

x64 main-thread hook mittels dll
 
Hallo,

ich versuche zur Zeit den main-thread einer Anwendung zu hooken.
Hierfür injecte ich die Dll ganz normal über CreateRemoteThread und rufe eine exportierte Funktion meiner Dll als Einstieg auf.

Die Dll versucht dann in den main-thread zu gelangen. Das funktioniert soweit auch, aber ab und an friert die Anwendung einfach ein, obwohl an der dll selber nichts verändert wurde.

Ich vermute das es Probleme mit dem Stack gibt.

Um in den main-thread zu gelangen verwende ich zunächst folgende 2 Funktionen, letztere ist sicher nicht perfekt vermute hier aber nicht den Fehler:

Code:
function GetMainThreadId: Cardinal;
var
  ThreadEntry: THREADENTRY32;
  hThreadSnapShot: THandle;
  dwProcessId: Cardinal;
begin
  result := 0;

  dwProcessId := GetCurrentProcessId();

  ThreadEntry.dwSize := SizeOf(THREADENTRY32);
  hThreadSnapShot := CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, dwProcessID);

  if (Thread32First(hThreadSnapShot, ThreadEntry)) then
  begin
    repeat
      if (ThreadEntry.th32OwnerProcessID = dwProcessID) then
      begin
        CloseHandle(hThreadSnapShot);
        result := ThreadEntry.th32ThreadID;
        exit;
      end;
    until Thread32Next(hThreadSnapshot, ThreadEntry) = false;
    CloseHandle(hThreadSnapShot);
  end;
end;

function HookThread(pFunction: Pointer; var OldEIP: NativeUInt): Boolean;
var
  hThread: THandle;
  dwMainThreadId: Cardinal;
  ctx: TContext;
const
  THREAD_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED or SYNCHRONIZE or $3FF;
begin
  dwMainThreadId := GetMainThreadId();

  if (dwMainThreadId = 0) then
    exit(false);

  hThread := OpenThread(THREAD_ALL_ACCESS, false, dwMainThreadId);

  if (hThread = 0) then
    exit(false);

  if (SuspendThread(hThread) = Cardinal(-1)) then
  begin
    CloseHandle(hThread);
    exit(false);
  end;

  ctx.ContextFlags := CONTEXT_CONTROL;

  if not (GetThreadContext(hThread, ctx)) then
  begin
    ResumeThread(hThread);
    CloseHandle(hThread);
    exit(false);
  end;

  OldEIP := ctx.Rip;
  ctx.Rip := NativeUInt(pFunction);

  if (SetThreadContext(hThread, ctx)) then
  begin
    ResumeThread(hThread);
    CloseHandle(hThread);
    result := true;
  end else
    result := false;
end;
Direkt nach dem injizieren wird mittels der beiden Funktionen dann folgender Assembler Code aufgerufen:

Delphi-Quellcode:
procedure AsmLoader; assembler;
asm
  sub    RSP, 8h
  push   RAX
  push   RBX
  push   RCX
  push   RDX
  push   RDI
  push   RSI
  push   RSP
  push   RBP
  push   R8
  push   R9
  push   R10
  push   R11
  push   R12
  push   R13
  push   R14
  push   R15
  mov RAX, _rip
  mov [RSP+128], RAX
  call _Load
  pop R15
  pop R14
  pop R13
  pop R12
  pop R11
  pop R10
  pop R9
  pop R8
  pop RBP
  pop RSP
  pop RSI
  pop RDI
  pop RDX
  pop RCX
  pop RBX
  pop RAX
end;
Load Funktion:

Delphi-Quellcode:
procedure _Load;
begin
  // sobald hier etwas aufgerufen wird kann es krachen
  GetCurrentThreadId();
end;

// Exported function
procedure Load(pArgs: Pointer);
begin
  if not (HookThread(@AsmLoader, _rip)) then
    ExitProcessMessage('Basic init failed');
end;
_rip ist ein NativeUint.

Den Assembler Code um die Register zu sichern und anschließend meine Funktion aufzurufen habe ich aus einem C++ Forum, der andere Part stammt von mir.

Ich bin bei x64 komplett neu, wäre nett wenn da mal jemand nachschauen könnte ob das alles so korrekt ist, und mir ggf. noch mitteilen kann wieso weshalb warum ;)

Es frieren übrigens alle Anwendungen ab und zu ein welche ich als Ziel benutze, kann auch das Notepad sein.

Grüße

Zacherl 1. Mai 2016 23:48

AW: x64 main-thread hook mittels dll
 
Darf man fragen, was du genau vor hast?

Unter x64 werden alleine schon von der calling-convention her nicht nur die GP-Register verwendet, sondern auch XMM, etc. Diese musst du ebenfalls saven und wieder restoren. Und das (R)/(E)FLAGS Register würde ich mir ebenfalls sichern, sofern du nicht 100%-ig garantieren kannst, dass deine aufgerufene Funktion die Flags nicht affektiert.

BUG 2. Mai 2016 08:30

AW: x64 main-thread hook mittels dll
 
Die Register und Flags sind zwar ein Problem, aber ein lösbares ... besonders wenn man sich mal anguckt was getThreadContext sichert ;)

Das kompliziertere Problem ist, dass du das Programm irgendwo unterbrichst. Wenn der gerade an einer Datenstruktur rummanipuliert und deine injizierte Funktion diese auch benutzt, kann es krachen. Ähnliches Problem wie mit Unix-Signal-Handlern.

Neutral General 2. Mai 2016 08:35

AW: x64 main-thread hook mittels dll
 
Bist du dir denn sicher, dass GetMainThreadId dir auch immer den Mainthread zurückgibt?
Du gehst ja davon aus, dass der erste Thread der für den Prozess gefunden wird der Mainthread ist. Ist das irgendwo dokumentiert?
Kann mir vorstellen, dass du manchmal einfach im falschen Thread landest.

Zacherl 2. Mai 2016 15:37

AW: x64 main-thread hook mittels dll
 
Zitat:

Zitat von Neutral General (Beitrag 1337265)
Bist du dir denn sicher, dass GetMainThreadId dir auch immer den Mainthread zurückgibt?
Du gehst ja davon aus, dass der erste Thread der für den Prozess gefunden wird der Mainthread ist. Ist das irgendwo dokumentiert?
Kann mir vorstellen, dass du manchmal einfach im falschen Thread landest.

Da es offiziell nichtmal sowas wie einen "Mainthread" gibt, ist deine Frage durchaus berechtigt :-D Ich gehe aber mal davon aus, dass er den Prozess suspendet erstellt und dann direkt seine Dll injected. In diesem Falle existiert eh nur ein einziger Thread. Allerdings kann er sich dann einiges an Arbeit ersparen, indem er einfach ein wenig anders vorgeht .. deshalb auch meine Frage, was er denn eigentlich vorhat.

@Clowdy:
Mit MSDN-Library durchsuchenQueueUserAPC kannst du einen APC registrieren, der netterweise direkt nach dem Resumen ausgeführt wird (natürlich nur in dem Falle, dass du den Prozess tatsächlich wie von mir vermutet im suspended State erstellst). Dieser APC würde dann auch im Kontext des angegebenen Threads ausgeführt (das scheint dir ja scheinbar wichtig zu sein).

Clowdy 2. Mai 2016 20:48

AW: x64 main-thread hook mittels dll
 
Hallo hallo! Danke für die doch zahlreichen Antworten. :)

Zitat:

Zitat von Neutral General (Beitrag 1337265)
Bist du dir denn sicher, dass GetMainThreadId dir auch immer den Mainthread zurückgibt?
Du gehst ja davon aus, dass der erste Thread der für den Prozess gefunden wird der Mainthread ist. Ist das irgendwo dokumentiert?
Kann mir vorstellen, dass du manchmal einfach im falschen Thread landest.

Ich bin mir relativ sicher mit der Funktion zumindest im richtigen zu landen.
Rufe ich in der _Load Funktion SetTimer() auf - was nicht mein Ziel ist, führen meine Aufrufe in der Callback Funktion in keinen Fall mehr zu einem Freeze. Rufe ich ich die gleichen Funktionen direkt in _Load auf, stürzt mir die Anwendung ab. Ich gehe also davon aus das nur der Assembler Code mit der Stack-Sicherung das Problem ist. Die ThreadId habe ich mit Breakpoints ohne das meine Dll injiziert ist noch einmal verifiziert. Der Aufruf erfolgt ausnahmslos vom ersten erstellten Thread.

Ich bin aber wahrlich noch kein Meister mit WinDbg.

Die Funktionen, oder eher ein Teil davon meiner Ziel-Anwendung sind nicht Thread sicher, daher der Umweg.

Zitat:

Zitat von Zacherl (Beitrag 1337297)
Da es offiziell nichtmal sowas wie einen "Mainthread" gibt, ist deine Frage durchaus berechtigt :-D Ich gehe aber mal davon aus, dass er den Prozess suspendet erstellt und dann direkt seine Dll injected. In diesem Falle existiert eh nur ein einziger Thread. Allerdings kann er sich dann einiges an Arbeit ersparen, indem er einfach ein wenig anders vorgeht .. deshalb auch meine Frage, was er denn eigentlich vorhat.

@Clowdy:
Mit MSDN-Library durchsuchenQueueUserAPC kannst du einen APC registrieren, der netterweise direkt nach dem Resumen ausgeführt wird (natürlich nur in dem Falle, dass du den Prozess tatsächlich wie von mir vermutet im suspended State erstellst). Dieser APC würde dann auch im Kontext des angegebenen Threads ausgeführt (das scheint dir ja scheinbar wichtig zu sein).

Nein ich kann auch zur Laufzeit injizieren, das Resultat bleibt aber das gleiche. Dennoch sieht die Funktion interessant aus, danke.
Zitat:

Zitat von Zacherl (Beitrag 1337257)
Darf man fragen, was du genau vor hast?

Unter x64 werden alleine schon von der calling-convention her nicht nur die GP-Register verwendet, sondern auch XMM, etc. Diese musst du ebenfalls saven und wieder restoren. Und das (R)/(E)FLAGS Register würde ich mir ebenfalls sichern, sofern du nicht 100%-ig garantieren kannst, dass deine aufgerufene Funktion die Flags nicht affektiert.

Zitat:

Zitat von BUG (Beitrag 1337263)
Die Register und Flags sind zwar ein Problem, aber ein lösbares ... besonders wenn man sich mal anguckt was getThreadContext sichert ;)

Das kompliziertere Problem ist, dass du das Programm irgendwo unterbrichst. Wenn der gerade an einer Datenstruktur rummanipuliert und deine injizierte Funktion diese auch benutzt, kann es krachen. Ähnliches Problem wie mit Unix-Signal-Handlern.

Danke für die Hinweise euch beiden, auch da werde ich mich noch einmal schlau machen. :stupid:
Habt mir schon mal sehr weitergeholfen.

Clowdy 13. Mai 2016 15:34

AW: x64 main-thread hook mittels dll
 
Hallo nochmal,

da durchaus der ein oder andere durch Google auf den Beitrag hier stoßen dürfte und die Resultate zu Thread Hijacking unter x64 ziemlich dürftig sind, möchte ich hier noch einmal den korrigierten Code posten. Ich habe den Code jetzt seit einer Woche ohne Probleme bei mir im Einsatz, möglich das er noch immer Fehler enthält, dafür übernehme ich keine Gewähr. :p

Hinzu kamen einige Probleme bei den Parametern einiger Funktionen bei der Anwendung für die ich meine DLL nutze, aber das dürfte eher uninteressant sein.

edit: Mit QueueUserAPC habe ich mich ebenfalls intensiv beschäftigt, scheidet hier aber leider aus da der Thread sich in einer Schleife befindet und nie in einem alertable state ist, das wäre also nur eine Lösung gewesen wenn ich direkt beim Start der Anwendung injiziere.

Delphi-Quellcode:
procedure AsmLoader; assembler;
asm
  push   [_rip]
  pushfq
  push   RAX
  push   RBX
  push   RCX
  push   RDX
  push   RDI
  push   RSI
  push   RSP
  push   RBP
  push   R8
  push   R9
  push   R10
  push   R11
  push   R12
  push   R13
  push   R14
  push   R15
  call _Load
  pop R15
  pop R14
  pop R13
  pop R12
  pop R11
  pop R10
  pop R9
  pop R8
  pop RBP
  pop RSP
  pop RSI
  pop RDI
  pop RDX
  pop RCX
  pop RBX
  pop RAX
  popfq
end;

Zacherl 13. Mai 2016 16:43

AW: x64 main-thread hook mittels dll
 
Der Vollsrändigkeit halber solltest du aber auf jeden Fall noch den State der XMM Register XMM0-XMM15 sichern. YMM, ZMM und die AVX512 Erweiterung mit X/Y/ZMM16-31 kann man denke ich außer Acht lassen, aber einige XMM Register werden für die Standard 64-Bit CallingConvention verwendet. Die würde ich deshalb auf jeden Fall noch sichern.

Clowdy 13. Mai 2016 17:38

AW: x64 main-thread hook mittels dll
 
Da muss ich mich tatsächlich selber noch einmal schlau machen, pushen geht ja nicht einfach so.
Ist bisher auch in keines der Beispiele die ich gefunden habe behandelt worden, allgemein unterscheiden sich die Lösungsansätze hier und da.

Ist aber ein guter Einwand. :wink:

Meinen aktuellen Lösungseinsatz habe ich übrigens von hier: http://shellblade.net/code-cave-windows.html

Zacherl 13. Mai 2016 18:10

AW: x64 main-thread hook mittels dll
 
Die AVX Register kannst du so pushen:
http://stackoverflow.com/a/10162065

Um ganz generisch zu sein, könntest du auch noch den FPU State sichern.


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:48 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz