Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Inject DLL from Memory (https://www.delphipraxis.net/120374-inject-dll-memory.html)

Megamorph 10. Sep 2008 00:52


Inject DLL from Memory
 
Hallo liebe Community,

ich suche ein Äquivalent zu der "StealthInject" Lib (C++) für Delphi.
Sie bietet die Möglichkeit eine DLL (in einem Stream,Buffer,String) direkt aus dem Memory heraus in einen fremden Prozess zu injecten - ohne Umweg über die Festplatte.

Alternativ würde mir auch eine Beschreibung oder ein Pseudocode, wie man so etwas selber macht, sehr helfen.

Danke im Vorraus
Megamorph

brechi 10. Sep 2008 06:40

Re: Inject DLL from Memory
 
In meiner uallCollection gibts da so eine funktion (jedenfalls ein InjectMe, und ich bin mir sicher, dass ich da auch mal so eine funktion zum injecten geschrieben habe. Wenn ich mich nicht irre kannst du LoadLibraryX oder so verwenden die lädt eine DLL in den Speicher, verwendet aber CreateFileA um die Dll zu laden und reserveirt selbst Speicher. Dieses kannst du dann ja weglassen)

Einfach googlen :)

Megamorph 11. Sep 2008 14:53

Re: Inject DLL from Memory
 
Hey,

danke für den Tipp.

Die Funktion InjectMe entsprach nicht ganz meiner Vorstellung. Ich möchte schon eine DLL injecten und keine EXE.
Jedoch habe ich einen Code in Anlehnung an die InjectLibrary geschrieben, welcher jedoch noch nicht ganz funktioniert.
Es wäre nett, wenn ihr mal einen Blick darüber werft, danke. (-;

Delphi-Quellcode:
procedure TForm1.Timer1Timer(Sender: TObject);
var
  BytesWritten, Process, Thread, ThreadId: cardinal;
  load_lib_param, body: pointer;
  DLL: pchar;
  szDll: string;
  dllptr : pointer;
  PID: cardinal;
begin
  if GetWindowThreadProcessID(FindWindow(nil,pchar('Unbenannt - Paint')),nil) = 0 then exit;
  GetWindowThreadProcessID(FindWindow(nil,pchar('Unbenannt - Paint')), PID);
  Timer1.Enabled:=false;

  tempstream.Seek(0, soFromBeginning);
  dllptr:=@tempstream;

  szdll:= ExtractFilePath(Application.ExeName) + 'test.dll';
  DLL := pchar (szdll);

  Process := OpenProcess(PROCESS_ALL_ACCESS, False, pid);

  load_lib_param := xVirtualAllocEx(Process, nil, 4096, MEM_COMMIT, PAGE_READWRITE);
  body := xVirtualAllocEx(Process, nil, tempstream.Size, MEM_COMMIT, PAGE_READWRITE);
  WriteProcessMemory(Process, load_lib_param, Pointer(DLL), 4096, BytesWritten);
  WriteProcessMemory(Process, body, dllptr, tempstream.size, BytesWritten);

  Thread := CreateRemoteThread(Process, nil, 0,
             GetProcAddress(GetModuleHandle('KERNEL32.DLL'), 'LoadLibraryA'),
             load_lib_param, 0, ThreadId);

  tempstream.Free;
  if Thread <> 0 then Memo1.Lines.Add('Injected!')
                 else Memo1.Lines.Add('Failed!');
  WaitForSingleObject(Thread, 6000);
  CloseHandle(Thread);
  CloseHandle(Process);
end;
Anmerkung:
1) tempstream enthält die DLL (mit SaveToFile getestet)
2) Die DLL befindet sich natürlich nicht in dem zusammengesetzten Pfad, aber man muss ja was übergeben ?!
3) kein Compiler-Error, kein Runtime-Error, die Ausgabe 'Injected!' kommt
4) Code in der DLL wird jedoch nicht ausgeführt

Hat jemand eine Idee?
Danke für Antworten (:
Megamorph

brechi 11. Sep 2008 19:45

Re: Inject DLL from Memory
 
so gehts nicht, die dll muss schon dann auch von deinem programm geladen sein (da z.B. die Adressen angepasst werden müssen)

Schau dir mal die ReadLibrary Funktion aus der uallkernel an, die macht eigentlich alles was man braucht. (Sections anpassen, ImportTabelle füllen, Relocation Adressen ändern)

Megamorph 11. Sep 2008 21:16

Re: Inject DLL from Memory
 
Hallo brechi,
ich sehe, dass in ReadLibrary die DLL von der Datei in den Memory geladen wird (an dieser Stelle ist es mir auch möglich statt dessen meinen FileStream zu verwenden).

Jedoch ist mir noch etwas unklar was der zweite Teil der Funktion macht bzw. was für eine Rolle die Funktion spielen soll, wenn ich die DLL in den fremden Prozess injecten möchte. :/

Kannst du mir da bitte noch einen kurzen Abriss geben?
Danke, Megamorph.

brechi 12. Sep 2008 12:03

Re: Inject DLL from Memory
 
Zuerst musst du schauen wieviel speicher du im Zielprozess für die Dll Brauchst (ImageSize von der Dll auslesen, funktion irgendwo in uallKernel).
Dann holst du dir dort den Speicher (VirtualAllocEx).

Dann musst du die Dll reinladen (ReadLibrary) und für die Adresse die du von VirtualAllocEx bekommen hast anpassen (ReloctaionSection etc. siehe ReadLibrary, eventl macht die das schon über einen Parameter).

Die DLl schreibst du dann mittels WriteProcessMemory in den Fremdprozess und Startest sie mittels CreateRemoteThread.

Megamorph 13. Sep 2008 15:20

Re: Inject DLL from Memory
 
Ich überlege mittlerweile sogar, das ganze Tool in C++ zu schreiben, weil es dort schöne Lib gibt und man mit 10 Zeilen Code drin ist...
Delphi wäre mir trotzdem wegen meiner etwas besseren Kenntnisse lieber.

Hier der Code in meinem Timer:
Delphi-Quellcode:
  tempstream.Seek(0, soFromBeginning);
  ImageSize:= tempstream.Size;
  body:= VirtualAllocEx(PID, nil, ImageSize, MEM_COMMIT, PAGE_READWRITE);
  dllptr:= Pointer(ReadLibrary2('',Cardinal(body)));
  WriteProcessMemory(PID, body, dllptr, ImageSize, BytesWritten);
  Thread := CreateRemoteThread(PID, nil, 0,
             GetProcAddress(GetModuleHandle('KERNEL32.DLL'), 'LoadLibraryA'),
             body, 0, ThreadId);
  if Thread <> 0 then Memo1.Lines.Add('Injected!')
                 else Memo1.Lines.Add('Failed!');
Hier die modifizierte ReadLibrary:
(alle Überprüfungen innerhalb der Funktion sind erfolgreich)
Delphi-Quellcode:
function ReadLibrary2(pLibraryName: PChar; OrigBase: DWord): DWord; stdcall;
var
  DllMain   : function (dwHandle, dwReason, dwReserved: DWord): DWord; stdcall;
  IDH       : PImageDosHeader;
  INH       : PImageNtHeaders;
  SEC       : PImageSectionHeader;
  dwread    : DWord;
  dwSecCount : DWord;
  dwFileSize : DWord;
  dwmemsize : DWord;
  i         : Integer;
  iFileHandle: Integer;
  pFileMem  : Pointer;
  pAll      : Pointer;
  SysDirP   : array [0..MAX_PATH-1] of Char;
  SysDir    : String;
begin
  Result := 0;
  (*
  iFileHandle := CreateFileA(pLibraryName,GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
  if (iFileHandle <= 0) then
  begin
    GetSystemDirectory(SysDirP, MAX_PATH);
    SysDir := SysDirP;
    iFileHandle := CreateFileA(PChar(SysDir+'\'+pLibraryName),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
    if (iFileHandle <= 0) then
      iFileHandle := CreateFileA(PChar(SysDir+'\DRIVERS\'+pLibraryName),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
      if (iFileHandle <= 0) then
        Exit;
  end;
  *)

  //dwFileSize := GetFileSize(iFileHandle,nil);
  dwFileSize := tempstream.Size;

  pFileMem := VirtualAlloc(nil,dwFileSize,MEM_COMMIT or MEM_RESERVE,PAGE_EXECUTE_READWRITE);
  if (pFileMem = nil) then
  begin
//    CloseHandle(iFileHandle);
    Exit;
  end;

  //ReadFile(iFileHandle,pFileMem^,dwFileSize,dwRead,nil);
  tempstream.Seek(0, soFromBeginning);
  getmem ( pFileMem,tempstream.Size);
  tempstream.Read(pFileMem^,tempstream.Size-1);

  IDH := pFileMem;
  if (isBadReadPtr(IDH,SizeOf(TImageDosHeader))) or
     (IDH^.e_magic <> IMAGE_DOS_SIGNATURE) then
  begin
    VirtualFree(pFileMem,dwFileSize,MEM_DECOMMIT);
    //CloseHandle(iFileHandle);
    Exit;
  end;

  INH := pointer(cardinal(pFileMem)+cardinal(IDH^._lfanew));
  if (isBadReadPtr(INH, SizeOf(TImageNtHeaders))) or
     (INH^.Signature <> IMAGE_NT_SIGNATURE) then
  begin
    VirtualFree(pFileMem,dwFileSize,MEM_DECOMMIT);
    //CloseHandle(iFileHandle);
    Exit;
  end;

  SEC := Pointer(Integer(INH)+SizeOf(TImageNtHeaders));
  dwMemSize := INH^.OptionalHeader.SizeOfImage;
  if (dwMemSize = 0) then
  begin
    VirtualFree(pFileMem,dwFileSize,MEM_DECOMMIT);
    //CloseHandle(iFileHandle);
    Exit;
  end;

  pAll := VirtualAlloc(nil,dwMemSize,MEM_COMMIT or MEM_RESERVE,PAGE_EXECUTE_READWRITE);
  if (pAll = nil) then
  begin
    VirtualFree(pFileMem,dwFileSize,MEM_DECOMMIT);
    //CloseHandle(iFileHandle);
    Exit;
  end;

  dwSecCount := INH^.FileHeader.NumberOfSections;
  CopyMemory(pAll,IDH,DWord(SEC)-DWord(IDH)+dwSecCount*SizeOf(TImageSectionHeader));

  for i := 0 to dwSecCount-1 do
  begin
    CopyMemory(Pointer(DWord(pAll)+SEC^.VirtualAddress),
      Pointer(DWord(pFileMem)+DWord(SEC^.PointerToRawData)),
      SEC^.SizeOfRawData);
    SEC := Pointer(Integer(SEC)+SizeOf(TImageSectionHeader));
  end;

  if (INH^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress <> 0) then
  ChangeReloc(Pointer(INH^.OptionalHeader.ImageBase),
              pAll,
              Pointer(DWord(pAll)+INH^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress),
              Pointer(OrigBase),
              INH^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size);
  CreateImportTable(pAll, Pointer(DWord(pAll)+INH^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress));

  @DllMain := Pointer(INH^.OptionalHeader.AddressOfEntryPoint+DWord(pAll));
  Result := DWord(pAll);
  //VirtualFree(pFileMem,dwFileSize,MEM_DECOMMIT);

  //CloseHandle(iFileHandle);
  Form1.Memo1.Lines.Add('bam');
end;
Leider kann ich ihn nicht dazu bewegen, dass im Memofeld 'Injected!' erscheint...
Hat vlt. noch jemand einen Tip?
Thx, Megamorph

brechi 15. Sep 2008 07:02

Re: Inject DLL from Memory
 
die Idee ist soweit richtig,
allersings ist imageSize <> stream.size
die Imagebase steht im PE-header der dll!

CreateRemoteThread muss beim EntryPoints anfangen nicht bei der dllbase (steht ebenfalls im PE header).

zu beidem sollte es von mir funktionen geben um es auszulesen

z.B. gibt @DllMain := Pointer(INH^.OptionalHeader.AddressOfEntryPoint+DW ord(pAll)); den Entry Point an
statt AddressOfEntryPoint sollte es ein ImageSize geben, welches du brauchst um die größe zu bestimmen

Scheib dafür am besten ReadLibrary um und übergib der Funktion das Prozesshandle

das sollte dann hinhauen wenn du nach

INH := pointer(cardinal(pFileMem)+cardinal(IDH^._lfanew)) ;

mitels INH^. Size und Entry bestimmt und dann erst den Speicher aus dem Prozess holst (Size!) und bei @dllmain dann den Thread (umgerechnet auf den Zielprozess startest)

Eventl!! (aber nur eventl) kann ich wenn ich zeit habe das heute abend mal umschreiben

brechi 15. Sep 2008 12:44

Re: Inject DLL from Memory
 
Vorläufige Version (in der Mittagspause gemacht, funktioniert aber noch nicht richtig, denke wegen den 3 Parametern bei DllMain)
Delphi-Quellcode:
function LoadLibraryEx(const fsLibrary: TStream; dwProcessID: DWord): Boolean; stdcall;
type
  TRelocBlock = packed record
    dwAddress: DWord;
    dwSize: DWord;
  end;
  PRelocBlock = ^TRelocBlock;

  TImportBlock = packed record
    dwCharacteristics: DWord;
    dwTimeDateStamp: DWord;
    dwForwarderChain: DWord;
    dwName: DWord;
    pFirstThunk: Pointer;
  end;
  PImportBlock = ^TImportBlock;

  TImportNameBlock = packed record
    wHint: Word;
    pName: PChar;
  end;
  PImportNameBlock = ^TImportNameBlock;

  procedure ChangeReloc(POrigBase, PBaseTemp, PReloc, PBaseTarget: Pointer; dwRelocSize: DWord); stdcall;
  var
    pCurrentRelocBlock: PRelocBlock;
    RelocCount: DWord;
    PCurrentStart: PWord;
    i: Integer;
    pRelocAddress: PInteger;
    iDif: Integer;
  begin
    pCurrentRelocBlock := PReloc;
    iDif := Integer(PBaseTarget) - Integer(POrigBase);
    PCurrentStart := Pointer(Integer(PReloc) + 8);
    while (not isBadReadPtr(pCurrentRelocBlock, SizeOf(TRelocBlock))) and
      (not isBadReadPtr(pCurrentStart, SizeOf(Pointer))) and
      (DWord(pCurrentRelocBlock) < DWord(pReloc) + dwRelocSize) do begin
      RelocCount := (pCurrentRelocBlock^.dwSize - 8) div SizeOf(Word);
      for i := 0 to RelocCount - 1 do begin
        if (not isBadReadPtr(pCurrentStart, SizeOf(Pointer))) and
          (PCurrentStart^ xor $3000 < $1000) then begin
          pRelocAddress := Pointer(pCurrentRelocBlock^.dwAddress + PCurrentStart^ mod $3000 + DWord(PBaseTemp));
          if (not isBadWritePtr(pRelocAddress, SizeOf(Integer))) then
            pRelocAddress^ := pRelocAddress^ + iDif;
        end;
        PCurrentStart := Pointer(DWord(PCurrentStart) + SizeOf(Word));
      end;
      pCurrentRelocBlock := Pointer(PCurrentStart);
      pCurrentStart := Pointer(DWord(PCurrentStart) + 8);
    end;
  end;

  procedure CreateImportTable(pLibraryHandle, pImportTable: pointer); stdcall;
  var
    pIBlock: PImportBlock;
    pThunksRead: PDWord;
    pThunksWrite: PDWord;
    pDllName: PChar;
    dwLibraryHandle: DWord;
    dwOldProtect: DWord;
  begin
    pIBlock := pImportTable;
    while (not isBadReadPtr(pIBlock, SizeOf(TImportBlock))) and
      (pIBlock^.pFirstThunk <> nil) and (pIBlock^.dwName <> 0) do begin
      pDllName := Pointer(DWord(pLibraryHandle) + DWord(pIBlock^.dwName));
      if (not isBadReadPtr(pDllName, 4)) then begin
        dwLibraryHandle := LoadLibrary(pDllName);
        pThunksRead := Pointer(DWord(pIBlock^.pFirstThunk) + DWord(pLibraryHandle));
        pThunksWrite := pThunksRead;
        if (DWord(pIBlock^.dwTimeDateStamp) = $FFFFFFFF) then
          pThunksRead := Pointer(DWord(pIBlock^.dwCharacteristics) + DWord(pLibraryHandle));
        while (not isBadReadPtr(pThunksRead, SizeOf(DWord))) and
          (not isBadReadPtr(pThunksWrite, SizeOf(Word))) and
          (pThunksRead^ <> 0) do begin
          if VirtualProtect(pThunksWrite, SizeOf(DWord), PAGE_EXECUTE_READWRITE, dwOldProtect) then begin
            if (DWord(pThunksRead^) and $80000000 <> 0) then
              pThunksWrite^ := DWord(GetProcAddress(dwLibraryHandle, PChar(pThunksRead^ and $FFFF)))
            else
              pThunksWrite^ := DWord(GetProcAddress(dwLibraryHandle, PChar(DWord(pLibraryHandle) + pThunksRead^ + SizeOf(Word))));
            VirtualProtect(pThunksWrite, SizeOf(DWord), dwOldProtect, dwOldProtect);
          end;
          Inc(pThunksRead);
          Inc(pThunksWrite);
        end;
      end;
      pIBlock := Pointer(DWord(pIBlock) + SizeOf(TImportBlock));
    end;
  end;

var
  DllMain: function(dwHandle, dwReason, dwReserved: DWord): DWord; stdcall;
  IDH: PImageDosHeader;
  INH: PImageNtHeaders;
  SEC: PImageSectionHeader;
  dwSecCount: DWord;
  dwmemsize: DWord;
  i: Integer;
  pFileMem: Pointer;
  pMemLocal: Pointer;
  pMemProcess: Pointer;
  hProcessHandle: THandle;
  dwWritten: DWord;
  hThread: THandle;
  dwThreadID: DWord;
begin
  Result := False;
  // ProcessHandle holen um Speicher für DLL zu allozieren und RemoteThread zu starten
  hProcessHandle := OpenProcess(PROCESS_ALL_ACCESS, false, dwProcessID);
  if hProcessHandle <> 0 then begin
    // für den Stream speicher holen um leichter auf die Daten zuzugreifen
    pFileMem := VirtualAlloc(nil, fsLibrary.Size, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (pFileMem <> nil) then begin
      fsLibrary.Position := 0;
      fsLibrary.Read(pFileMem^, fsLibrary.Size);
      IDH := pFileMem;
      // ImageDosHeader gültig?
      if (not isBadReadPtr(IDH, SizeOf(TImageDosHeader))) and
        (IDH^.e_magic = IMAGE_DOS_SIGNATURE) then begin
        // ImageNtHeader gültig?
        INH := pointer(cardinal(pFileMem) + cardinal(IDH^._lfanew));
        if (not isBadReadPtr(INH, SizeOf(TImageNtHeaders))) and
          (INH^.Signature = IMAGE_NT_SIGNATURE) then begin
          SEC := Pointer(Integer(INH) + SizeOf(TImageNtHeaders));

          // Virtuelle größe der Dll/Exe ermitteln
          dwMemSize := INH^.OptionalHeader.SizeOfImage;
          if (dwMemSize <> 0) then begin
            // Lokalen speicher für die DLL holen (wir bereiten die in userem Prozess vor)
            pMemLocal := VirtualAlloc(nil, dwMemSize, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
            if (pMemLocal <> nil) then begin
              // Speicher aus dem Fremdprozess holen (indem wir die dll nachher kopieren und ausführen)
              pMemProcess := VirtualAllocEx(hProcessHandle, nil, dwMemSize, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
              if pMemProcess <> nil then begin

                // jede Sektion and die richtige Adresse kopieren
                dwSecCount := INH^.FileHeader.NumberOfSections;
                CopyMemory(pMemLocal, IDH, DWord(SEC) - DWord(IDH) + dwSecCount * SizeOf(TImageSectionHeader));
                for i := 0 to dwSecCount - 1 do begin
                  CopyMemory(Pointer(DWord(pMemLocal) + SEC^.VirtualAddress),
                    Pointer(DWord(pFileMem) + DWord(SEC^.PointerToRawData)),
                    SEC^.SizeOfRawData);
                  SEC := Pointer(Integer(SEC) + SizeOf(TImageSectionHeader));
                end;

                // Adressen der Dll für den Zielprozess anpassen
                if (INH^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress <> 0) then begin
                  ChangeReloc(Pointer(INH^.OptionalHeader.ImageBase),
                    pMemLocal,
                    Pointer(DWord(pMemLocal) + INH^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress),
                    Pointer(pMemProcess),
                    INH^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size);
                end;

                // die Importtabelle anpassen, die Adressen holen wir lokal aus unserem Prozess
                // sollte die Dll nicht im Zielprozess geladen sein -> crash
                // theoretisch kann man diese auch aus dem Zielprozess ermitteln
                // und fehlende Dlls dort ebenfalls nachladen
                // aber jetzt nicht hier behandelt, da dll die in einen Fremdprozess injeziert werden
                // IMMER nur ntdll/kernel32/(user32 falls Prozess mit Fenster) Funktionen benutzen sollen
                CreateImportTable(pMemLocal, Pointer(DWord(pMemLocal) + INH^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress));

                // bei DLL-Main einen Thread starten
                @DllMain := Pointer(INH^.OptionalHeader.AddressOfEntryPoint + DWord(pMemProcess));
                if WriteProcessMemory(hProcessHandle, pMemProcess, pMemLocal, dwMemSize, dwWritten) and (dwWritten = dwMemSize) then begin
                  hThread := CreateRemoteThread(hProcessHandle, nil, 0, @DllMain, nil, 0, dwThreadID);
                  if hThread <> 0 then begin
                    Result := True;
                    CloseHandle(hThread);
                  end;
                end else begin
                  VirtualFreeEx(hProcessHandle, pMemProcess, dwMemSize, MEM_DECOMMIT);
                end;
              end;
            end;
            VirtualFree(pMemLocal, dwMemSize, MEM_DECOMMIT);
          end;
        end;
        VirtualFree(pFileMem, fsLibrary.Size, MEM_DECOMMIT);
      end;
    end;
    CloseHandle(hProcessHandle);
  end;
end;

procedure TForm12.FormCreate(Sender: TObject);
var
  fm: TFileStream;
begin
  fm := TFileStream.Create(ExtractFilePath(Paramstr(0))+'test.dll', fmOpenRead);
  LoadLibraryEx(fm, GetCurrentProcessID);
  fm.Free;
end;
jep liegt an dem RemoteThread, dies funcktioniert:
muss man noch bisl Code injezieren um DllMain richtig aufzurufen
vill schaffst das selber, ansonsten meld ich mich vill heute abend

Delphi-Quellcode:
                  //hThread := CreateRemoteThread(hProcessHandle, nil, 0, @DllMain, nil, 0, dwThreadID);
                  DllMain(0, DLL_PROCESS_ATTACH, 0);

Megamorph 15. Sep 2008 16:19

Re: Inject DLL from Memory
 
Hallo brechi,

ich danke dir zuerst vielmals für deine Hilfe! (-:

Ich mache mich gleich ans Testen, jedoch muss ich gleich nochmal von 17:30-20:00 Uhr weg und werde dann spätestens 22:00Uhr meine Ergenbnisse in den Post editieren.

edit: bisher noch nichts - ich mache morgen weiter

edit: Ich denke auch, dass es an den 3 Parametern der DLLMain liegt. Wie wäre es eine eigene Funktion zu schreiben, die Adresse dieser (zb. mit OllyDBG) rauszufinden und dann die zu starten?

Bis dahin,
Megamorph

Megamorph 17. Sep 2008 18:04

Re: Inject DLL from Memory
 
Hallo brechi,

ich habe jetzt eine zusätzliche Funktion in die DLL implementiert, welche als EP dienen soll (mit 1 und 0 Parametern getestet), was aber leider nicht funktioniert hat.

Hast du vlt. noch eine andere Idee, wie wir das zum Laufen bekommen?

MfG, Megamorph

brechi 17. Sep 2008 18:09

Re: Inject DLL from Memory
 
Ich könnte dir das natürlich programmieren, aber das wäre ja nicht sinn der Sache und hab auch nicht so viel Zeit. Und ich wieß ja noch nicht einmal für welches Projekt das überhaupt ist, ist es OpenSource dann würd ich dir das vill noch machen.

Megamorph 18. Sep 2008 17:39

Re: Inject DLL from Memory
 
Hallo brechi,

das stimmt natürlich - ich habe dir noch garnichts von meinem eigentlichen Vorhaben erzählt:

Auf der Suche nach einem portablen TaskManger, welcher auch die einzelnen Prozesse etwas näher beleuchtet, bin ich zb. auf SystemExplorer 1.4 gekommen. Die Funde waren zwar teilweise gute Programme, jedoch war die Analyse eines bestimmten Prozesses nicht so tiefgründig, wie ich es mir gerne wünsche. Das liegt bestimmt nicht an mangelnden Möglichkeiten oder Programmierkenntnissen der Ersteller - es würde einfach den Rahmen der jeweiligen Software sprängen.

Also habe ich mir überlegt, dass ich selber so einen portablen TaskAnalyzer baue. Das Programm soll aus nur einer einzigen Executablen bestehen und aus Sicherheitsgründen auch nirgendwohin Programmteile, Libs oder DLLs temporär "entpacken" und benutzen.

Zu der tiefgründigen Analyse muss ich eine DLL injecten (durchaus üblich, siehe diverse AntiVir: Kaspersky, F-Secure und co.). Der Analyzer soll zb. Aufschluss über DateiHandles/IO-Aktivitäten, IPC's, Pipes, Netzwerk-Zugriffe, Registryzugriffe, geladene Module, verwendete verdächtige Funktionen bzw. Funktionskombinationen, ... geben und soll das Abschießen von hartnäckigen verdächtigen Prozessen erlauben (notfalls Neustart + Autostart delte + Datei delete).

Bisher habe ich mir noch keine Gedanken zur Veröffentlichung des Source Codes gemacht, jedoch finde ich die Idee sehr anreizend. Wenn man sich überlegt, dass vlt. noch ein paar andere Coder gute Ideen haben, wie man den Analyzer bereichern kann. Wenn eine gewisse Grundbasis fertig gestellt ist, werde ich ein Projekt auf SourceForge eröffnen und den Source öffentlich verfügbar machen.

MfG, Megamorph

brechi 19. Sep 2008 16:25

Re: Inject DLL from Memory
 
Delphi-Quellcode:
function LoadLibraryEx(const fsLibrary: TStream; dwProcessID: DWord): Boolean; stdcall;
type
  TRelocBlock = packed record
    dwAddress: DWord;
    dwSize: DWord;
  end;
  PRelocBlock = ^TRelocBlock;

  TImportBlock = packed record
    dwCharacteristics: DWord;
    dwTimeDateStamp: DWord;
    dwForwarderChain: DWord;
    dwName: DWord;
    pFirstThunk: Pointer;
  end;
  PImportBlock = ^TImportBlock;

  TImportNameBlock = packed record
    wHint: Word;
    pName: PChar;
  end;
  PImportNameBlock = ^TImportNameBlock;

  procedure ChangeReloc(POrigBase, PBaseTemp, PReloc, PBaseTarget: Pointer; dwRelocSize: DWord); stdcall;
  var
    pCurrentRelocBlock: PRelocBlock;
    RelocCount: DWord;
    PCurrentStart: PWord;
    i: Integer;
    pRelocAddress: PInteger;
    iDif: Integer;
  begin
    pCurrentRelocBlock := PReloc;
    iDif := Integer(PBaseTarget) - Integer(POrigBase);
    PCurrentStart := Pointer(Integer(PReloc) + 8);
    while (not isBadReadPtr(pCurrentRelocBlock, SizeOf(TRelocBlock))) and
      (not isBadReadPtr(pCurrentStart, SizeOf(Pointer))) and
      (DWord(pCurrentRelocBlock) < DWord(pReloc) + dwRelocSize) do begin
      RelocCount := (pCurrentRelocBlock^.dwSize - 8) div SizeOf(Word);
      for i := 0 to RelocCount - 1 do begin
        if (not isBadReadPtr(pCurrentStart, SizeOf(Pointer))) and
          (PCurrentStart^ xor $3000 < $1000) then begin
          pRelocAddress := Pointer(pCurrentRelocBlock^.dwAddress + PCurrentStart^ mod $3000 + DWord(PBaseTemp));
          if (not isBadWritePtr(pRelocAddress, SizeOf(Integer))) then
            pRelocAddress^ := pRelocAddress^ + iDif;
        end;
        PCurrentStart := Pointer(DWord(PCurrentStart) + SizeOf(Word));
      end;
      pCurrentRelocBlock := Pointer(PCurrentStart);
      pCurrentStart := Pointer(DWord(PCurrentStart) + 8);
    end;
  end;

  procedure CreateImportTable(pLibraryHandle, pImportTable: pointer); stdcall;
  var
    pIBlock: PImportBlock;
    pThunksRead: PDWord;
    pThunksWrite: PDWord;
    pDllName: PChar;
    dwLibraryHandle: DWord;
    dwOldProtect: DWord;
  begin
    pIBlock := pImportTable;
    while (not isBadReadPtr(pIBlock, SizeOf(TImportBlock))) and
      (pIBlock^.pFirstThunk <> nil) and (pIBlock^.dwName <> 0) do begin
      pDllName := Pointer(DWord(pLibraryHandle) + DWord(pIBlock^.dwName));
      if (not isBadReadPtr(pDllName, 4)) then begin
        dwLibraryHandle := LoadLibrary(pDllName);
        pThunksRead := Pointer(DWord(pIBlock^.pFirstThunk) + DWord(pLibraryHandle));
        pThunksWrite := pThunksRead;
        if (DWord(pIBlock^.dwTimeDateStamp) = $FFFFFFFF) then
          pThunksRead := Pointer(DWord(pIBlock^.dwCharacteristics) + DWord(pLibraryHandle));
        while (not isBadReadPtr(pThunksRead, SizeOf(DWord))) and
          (not isBadReadPtr(pThunksWrite, SizeOf(Word))) and
          (pThunksRead^ <> 0) do begin
          if VirtualProtect(pThunksWrite, SizeOf(DWord), PAGE_EXECUTE_READWRITE, dwOldProtect) then begin
            if (DWord(pThunksRead^) and $80000000 <> 0) then
              pThunksWrite^ := DWord(GetProcAddress(dwLibraryHandle, PChar(pThunksRead^ and $FFFF)))
            else
              pThunksWrite^ := DWord(GetProcAddress(dwLibraryHandle, PChar(DWord(pLibraryHandle) + pThunksRead^ + SizeOf(Word))));
            VirtualProtect(pThunksWrite, SizeOf(DWord), dwOldProtect, dwOldProtect);
          end;
          Inc(pThunksRead);
          Inc(pThunksWrite);
        end;
      end;
      pIBlock := Pointer(DWord(pIBlock) + SizeOf(TImportBlock));
    end;
  end;

  procedure StartDll(dwDllHandle: DWord); stdcall;
  asm
    MOV EBX, DWORD PTR [dwDllHandle]
    CALL @@J
    @@J:
    POP EAX
    AND EAX, $FFFFF000
    MOV EAX, DWORD PTR [EAX]
    PUSH 0
    PUSH DLL_PROCESS_ATTACH
    PUSH EBX
    CALL EAX
  end;
  procedure EndDll; asm end;


var
  DllMain: function(dwHandle, dwReason, dwReserved: DWord): DWord; stdcall;
  IDH: PImageDosHeader;
  INH: PImageNtHeaders;
  SEC: PImageSectionHeader;
  dwSecCount: DWord;
  dwmemsize: DWord;
  i: Integer;
  pFileMem: Pointer;
  pMemLocal: Pointer;
  pMemProcess: Pointer;
  hProcessHandle: THandle;
  dwWritten: DWord;
  hThread: THandle;
  dwThreadID: DWord;
  ThreadStart: function(_Param: Pointer): Integer; stdcall;
begin
  Result := False;
  // ProcessHandle holen um Speicher für DLL zu allozieren und RemoteThread zu starten
  hProcessHandle := OpenProcess(PROCESS_ALL_ACCESS, false, dwProcessID);
  if hProcessHandle <> 0 then begin
    // für den Stream speicher holen um leichter auf die Daten zuzugreifen
    pFileMem := VirtualAlloc(nil, fsLibrary.Size, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (pFileMem <> nil) then begin
      fsLibrary.Position := 0;
      fsLibrary.Read(pFileMem^, fsLibrary.Size);
      IDH := pFileMem;
      // ImageDosHeader gültig?
      if (not isBadReadPtr(IDH, SizeOf(TImageDosHeader))) and
        (IDH^.e_magic = IMAGE_DOS_SIGNATURE) then begin
        // ImageNtHeader gültig?
        INH := pointer(cardinal(pFileMem) + cardinal(IDH^._lfanew));
        if (not isBadReadPtr(INH, SizeOf(TImageNtHeaders))) and
          (INH^.Signature = IMAGE_NT_SIGNATURE) then begin
          SEC := Pointer(Integer(INH) + SizeOf(TImageNtHeaders));

          // Virtuelle größe der Dll/Exe ermitteln
          dwMemSize := INH^.OptionalHeader.SizeOfImage;
          if (dwMemSize <> 0) then begin
            // Lokalen speicher für die DLL holen (wir bereiten die in userem Prozess vor)
            pMemLocal := VirtualAlloc(nil, dwMemSize, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
            if (pMemLocal <> nil) then begin
              // Speicher aus dem Fremdprozess holen (indem wir die dll nachher kopieren und ausführen)
              pMemProcess := VirtualAllocEx(hProcessHandle, nil, dwMemSize+$1000, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
              if pMemProcess <> nil then begin

                // jede Sektion and die richtige Adresse kopieren
                dwSecCount := INH^.FileHeader.NumberOfSections;
                CopyMemory(pMemLocal, IDH, DWord(SEC) - DWord(IDH) + dwSecCount * SizeOf(TImageSectionHeader));
                for i := 0 to dwSecCount - 1 do begin
                  CopyMemory(Pointer(DWord(pMemLocal) + SEC^.VirtualAddress),
                    Pointer(DWord(pFileMem) + DWord(SEC^.PointerToRawData)),
                    SEC^.SizeOfRawData);
                  SEC := Pointer(Integer(SEC) + SizeOf(TImageSectionHeader));
                end;

                // Adressen der Dll für den Zielprozess anpassen
                if (INH^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress <> 0) then begin
                  ChangeReloc(Pointer(INH^.OptionalHeader.ImageBase),
                    pMemLocal,
                    Pointer(DWord(pMemLocal) + INH^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress),
                    Pointer(pMemProcess),
                    INH^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size);
                end;

                // die Importtabelle anpassen, die Adressen holen wir lokal aus unserem Prozess
                // sollte die Dll nicht im Zielprozess geladen sein -> crash
                // theoretisch kann man diese auch aus dem Zielprozess ermitteln
                // und fehlende Dlls dort ebenfalls nachladen
                // aber jetzt nicht hier behandelt, da dll die in einen Fremdprozess injeziert werden
                // IMMER nur ntdll/kernel32/(user32 falls Prozess mit Fenster) Funktionen benutzen sollen
                CreateImportTable(pMemLocal, Pointer(DWord(pMemLocal) + INH^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress));

                // bei DLL-Main einen Thread starten
                @DllMain := Pointer(INH^.OptionalHeader.AddressOfEntryPoint + DWord(pMemProcess));
                if WriteProcessMemory(hProcessHandle, pMemProcess, pMemLocal, dwMemSize, dwWritten) and (dwWritten = dwMemSize) and
                   WriteProcessMemory(hProcessHandle, Pointer(DWord(pMemProcess)+dwMemSize), @@DllMain, 4, dwWritten) and (dwWritten = 4) and
                   WriteProcessMemory(hProcessHandle, Pointer(DWord(pMemProcess)+dwMemSize+4), @StartDll, Integer(@EndDll)-Integer(@StartDll), dwWritten) then begin
                  @ThreadStart := Pointer(DWord(pMemProcess)+dwMemSize+4);
                  //ThreadStart(nil);
                  hThread := CreateRemoteThread(hProcessHandle, nil, 0, @ThreadStart, nil, 0, dwThreadID);
                  if hThread <> 0 then begin
                    Result := True;
                    CloseHandle(hThread);
                  end;
                end else begin
                  VirtualFreeEx(hProcessHandle, pMemProcess, dwMemSize+$1000, MEM_DECOMMIT);
                end;
              end;
            end;
            VirtualFree(pMemLocal, dwMemSize, MEM_DECOMMIT);
          end;
        end;
        VirtualFree(pFileMem, fsLibrary.Size, MEM_DECOMMIT);
      end;
    end;
    CloseHandle(hProcessHandle);
  end;
end;
Davon ab verstehe ich aber nicht warum du nicht InjectMe verwendest. Diese ist genua dafür gedacht dll&exe in einem Programm zu haben und die Exe dann zu injecten wobei dort dann andere Funktionen (die z.b. von der Dll) ausgeführt werden können. Gerade jetzt mach ich mir wieder Gedanken ob ich den Code oben umsonst programmiert habe. Jedenfalls hätte ich dir direkt zu InjectMe geraten wenn du mir dein Anliegen erklärt hättest.

Metal_Snake2 19. Sep 2008 17:32

Re: Inject DLL from Memory
 
Wollte fragen funktioniert das jetzt?

Also wenn ja dan ab damit in die Code- Library würde ich sagen.
So eine funktion kann man immer (so wie ich jetzt) wieder gebrauchen.

Edit: Wie könnte man die Injezierte Dll aus dem Prozess entfernen?

Megamorph 20. Sep 2008 09:39

Re: Inject DLL from Memory
 
Hallo,

ich bin noch etwas am probieren, deswegen habe ich mich noch nicht gemeldet. Mit einer ganz einfachen DLL (Application.MessageBox...) klappt es auf jeden Fall schon. Jedoch kann man kaum andere Funktionen benutzen, dann stürzt der Prozess beim injecten immer gleich ab. Man muss seine DLL's auf jeden Fall darauf zuschneiden. Aber wie gesagt - ich bin noch am Testen.

@brechi: Vielleicht wäre die Funktion ja auch eine sinnvolle Erweiterung für deine uall Collection?

edit:
@brechi: Die Funktion war auf jeden fall nicht unnütz, da ich auch dll's benutze, deren Code ich nicht kenne --> InjectMe schaltet aus

MfG Megamorph

brechi 20. Sep 2008 11:01

Re: Inject DLL from Memory
 
Es sollten nur Funktionen aus der kernel32.dll verwendet werden, dann sollte es funktionieren. Wenn du eine Dll hast, die nicht wirklich viel macht und nicht funktioniiert dann her damit :) ich würds mir dann mal anschauen.

Megamorph 20. Sep 2008 13:25

Re: Inject DLL from Memory
 
Hallo,

zb. ein einfaches ShowMessage funktioniert nicht, Application.MessageBox geht allerdings.

Ansonsten "mache ich sehr viel" in der DLL: haufenweise Informationen aus dem Prozess holen, Prozessspeicher komplett rauskopieren (die Analyse code ich später), etc..

MfG Megamorph

Win32.API 20. Sep 2008 13:49

Re: Inject DLL from Memory
 
Wenn du so wenig Wissen in diesem Bereich hast, solltest du dir das Wissen aneignen, oder das Projekt vergessen.

Oder soll brechi das gesamte Projekt fuer dich vollenden? :roll:

brechi 20. Sep 2008 14:09

Re: Inject DLL from Memory
 
Du verstößt gegen die Hook Regeln. Keine VCL benutzen. Nur Funktionen aus der Kernel32.dll. D.h. fü den Fall MessageBoxA. (Ist gernell so ob du nu ne dll injezierst odre nicht). *Eigentlich* sollte es funktionieren aber kA was Delphi da alles macht in der VCL.

Megamorph 20. Sep 2008 20:37

Re: Inject DLL from Memory
 
Hallo,

alle DLL's die ich vergeblich versucht habe mit ReadLibraryEx zu injecten, habe ich zuvor alle erfolgreich mit normalem Injecten getestet.

@Win32.API: keines Wegs - es geht mir nur um das Injecten der DLL. Viele Funktionen, die sich insbesondere mit der Analyse beschäftigen, funktionieren schon. Wenn ich im Vorhinein gewusst hätte, welcher Aufwand das wird, hätte ich das wahrscheinlich eh Niemanden angetan - an der Stelle nochmal ein großes Danke an brechi.

Zitat:

zb. ein einfaches ShowMessage funktioniert nicht, Application.MessageBox geht allerdings.
Ich habe einfach unterschiedliche Funktionen getestet und hier das aufgeschrieben, was ich noch im Kopf hatte.

MfG, Megamorph

Metal_Snake2 20. Sep 2008 23:24

Re: Inject DLL from Memory
 
Also bei mir funktioniert das leider nicht. Ich habe eine Dll als res. und möchte sie nun ohne es auf die Festplatte zu schreiben injezieren jedoch passiert nichts.

Megamorph 21. Sep 2008 08:58

Re: Inject DLL from Memory
 
Hallo,

hast du sie zuvor in einen MemoryStream kopiert?

MfG Megamorph

brechi 21. Sep 2008 12:32

Re: Inject DLL from Memory
 
Dadurch, dass du keine DLL mehr hast, aknn es natürlich sein, dass einige Funktionen nicht mehr richtig funktionieren. Aber generell sollte schon keine VCL innerhalb der Dll verwendet werden!

SirThornberry 21. Sep 2008 12:56

Re: Inject DLL from Memory
 
Wozu braucht man soetwas eigentlich? Wofür macht man sich den Aufwand anstelle einfach eine DLL von der Festplatte zu laden?

Megamorph 21. Sep 2008 17:02

Re: Inject DLL from Memory
 
Hallo,

ging mir um die Portabilität und natürlich auch um Sicherheitsaspekte.

MfG Megamorph

Zacherl 21. Sep 2008 19:53

Re: Inject DLL from Memory
 
Sicherheitsaspekte? Was ist denn daran sicherer?

Metal_Snake2 23. Sep 2008 18:59

Re: Inject DLL from Memory
 
Ich teste es gerade und will es von ner res. DLL aus injecten nur funzt es bis jetzt nicht.
Die Dll hat keine exportierten Funktionen.

hier mein code bis jetzt:
Delphi-Quellcode:
procedure TfrmMain.StealthInject;
var
  Res: TResourceStream;
  mem: TMemoryStream;
begin
  if FindResource(hInstance, PChar(DLLName), 'BINRES') <> 0 then
    begin
      Res := TResourceStream.Create(hInstance, DLLName, 'BINRES');
      try
        Mem := TMemorystream.Create;
        try
          mem.CopyFrom(res, res.Size);
          LoadLibraryEx(mem, ProcessPID('explorer.exe'));
        finally
          mem.free;
        end;
      finally
        Res.Free;
      end;
    end;
end;
hmm ich komme einfach nicht weiter, was mache ich hier falsch?


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:26 Uhr.

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