Ergebnis 1 bis 7 von 7
  1. #1
    Superman Avatar von MarcoPeterino
    Registriert seit
    27.10.2008
    Beiträge
    439

    Lächeln Delphi Simpler PE-Crypter

    Hiho liebe Leute.

    Ich möchte euch heute erklären, wie man in Delphi einen simplen Crypter schreibt !

    In diesem Tutorial wird erklärt wie man die ".text" Section einer Exe Datei verschlüsselt, den Einsprungspunkt auf eine von uns selbst geschrieben (ASM) Procedure/Code umleitet, der die Section wieder entschlüsselt und zum orginalen Einsprungspunkt zurückkehrt !

    Voraussetzungen:

    • Fortgeschrittene Kenntnisse in Delphi
    • Datenstrukturen und Pointer müsstet ihr gut beherrschen
    • Assembler sollte kein Fremdwort sein



    Inhaltsangabe:

    1. Wichtige Details über eine Exe bzw den verschiedenen Headers
    2. Ungefähres Design des Programmes
    3. Das Problem mit den Adressen
    4. Die Implementierung[/b]





    1. Wichtige Details über eine Exe bzw den verschiedenen Headers

    Ich hatte nicht gewusst, dass die einzelnen Headers in Delphi schon vordefiniert sind und deshalb
    machte ich mich auf die Suche nach bestimmten Artikeln, die diese beschrieben ! Da boten sich einige gute an ! Ihr könnt euch die von wotsit.org besorgen ! Sucht dort nach "PE-Header"
    bzw Portable Executable..

    Eine Exe ist wie folgt aufgebaut:
    (wichtige Teile)

    • Dos-Header (Delphi: IMAGE_DOS_SIGNATURE);
    • ...Müll...
    • NT-Header(s) (Delphi: IMAGE_NT_HEADERS)
    • Sections (Delphi: _IMAGE_SECTION_HEADER);
      (unwichtig...)
    • Image Pages
    • import info
    • export info
    • fixup info
    • resource info
    • debug info



    Ihr solltet euch diese drei Strukturen einmal genauer anschauen:

    Der Dos-Header ist simple aufgebaut und steht am Anfang der Exe:


    // alle hier vorgestellten Typen habe ich aus den Dokumentationen extrahiert !
    Signature : Array[0..1] of Char; // 4Dh 5Ah (MZ)
    LastPageSize : Word; // number of bytes in last page
    FilePages : Word; // number of 512 byte pages
    RelocItems : Word; // number of entries in table
    HeaderParas : Word; // size of header in 16 byte paras
    MinAlloc : Word; // minimum memory required in paras
    MaxAlloc : Word; // maximum memory wanted in paras
    PreRelocSS : Word; // offset in paras to stack segment
    InitialSP : Word; // starting SP value
    NegativeChecksum : Word; // currently ignored
    PreRelocIP : Word; // execution start address
    PreRelocCS : Word; // preadjusted start segment
    RelocTableOffset : Word; // is offset from start of file)
    OverlayNumber : Word; // ignored if not overlay
    Nothing : Cardinal; // end ...
    Reserved_Unused : Array[0..6] of Cardinal;
    PEOff : Cardinal; // offset of the header at off 60

    Für uns sind folgende Einträge wichtig:

    • Signature - zum Ãœberprüfen, ob es sich ume eine Exe handelt (bzw Dos Datei)
    • PEOff - hier ist der Offset zum eigentlichen Header - dem NT_Header



    Der NT-Header (Delphi: IMAGE_NT_HEADERS) besteht seinerseits wiederum aus drei Teilen:


    • Signature
    • FileHeader]
    • OptionalHeader




    Für uns sind unter Umständen folgende Einträge wichtig:
    FileHeader:
    o NumberOfSections - dazu komme ich später

    OptionalHeader:
    o AddressOfEntryPoint - Einsprungspunkt
    o ImageBase - die Adresse, wo die Exe (Image) hingeladen wird

    Dann gibts da noch die Sections ! Im Fileheader steht die Anzahl der Sections nämlich in NumberOfSections !
    Delphi Deklaration von einer Section:
    _IMAGE_SECTION_HEADER

    Da der Eintrag "Misc" euch ein wenig verwirren kann, werde ich euch auch diese Struktur erklären


    TSection_Table = packed record
    Object_Name : Array[0..7] of Char;
    Virtual_Size : Cardinal;
    RVA : Cardinal;
    Physical_Size : Cardinal;
    Physical_Offset : Cardinal;
    Reloc_Offset : Cardinal;
    Line_Numbers : Cardinal;
    Relocation_Number : Word;
    Line_Numbers_Number : Word;
    Object_Flags : Cardinal;
    end;


    für uns sind wiedermal folgende Sachen wichtig:
    o Object_Name = beinhaltet, so wie der Name schon sagt, den Namen der Section
    o Virtual_Size = gibt die virtuelle Größe der Section an (die Größe die es nach dem Laden ins Memory hat)
    o RVA = Relative Virtual Address = die Adresse im Memory
    Ihr fragt euch nun "auf was relativ" ?
    Relativ auf die ImageBase-Adresse !!
    o Physical_Size = die Größe in der Exe
    o Physical_Offset = Adresse in der Exe
    o Object_Flags = Nähere Definition, um was für eine Section es sich handelt - ob es beschreibbar/lesbar/ausführbar/... ist



    2. Ungefähres Design des Programmes

    Der Crypter muss folgende Schritte durcharbeiten:
    1. Lese die komplette Exe ein
    2. Prüfe ob die DOS.Signature von der Exe "MZ" lautet - wenn ja, dann fahre fort
    3. Ermittle den PEOffset
    4. Prüfe ob die NTH.Signature von der Exe "PE" lautet - wenn ja, dann fahre fort
    Ermittle wichtige Informationen:
    5. ImageBase
    6. EntryPoint
    7. NumberOfSections
    8. Offset von ".text" - Section
    9. ermitle die einzelnen Parameter dieser Section
    10. Prüfe, ob diese Section einen Code-Cave hat UND ob diese >= unserem (ASM) Decrypter ist
    11. Crypte diese Section (in unserem Fall mit Xor MagicNumber)
    12. Passe unseren ASM-Code an
    13. Füge den ASM-Code an das Ende der Section hinzu
    14. setze den neuen Einsprungspunkt (Entrypoint) und passe ggf. Werte an

    Im Großen und Ganzen wars das auch schon !



    3. Das Problem mit den Adressen

    Hier gibts eigentlich nicht viel zu sagen. Ich werds trotzdem tun, da
    irgendwie viele an diesem Punkt scheitern:

    Um die Adresse einer Sections im Speicher ermitteln zu können müsst
    ihr die Imagebase von der Exe kennen und ihr braucht noch die VirtualAddress
    der Section !

    Die Adresse im Speicher = ImageBase + Section.VirtualAddress



    4. Die Implementierung

    Folgende Codeschnipsel habe ich aus meinem Projekt, welches ich mit angehangen habe, entnommen !

    1. Diesen Schritt müsstet ihr schon alleine hinkriegen xD
    Ich empfehle euch, die Daten in ein ByteArray (TByteArr = Array of Byte) zu speichern, da folgende Codeschnipsel darauf aufbauen !

    2. Einfach schauen, ob die ersten 2 Bytes = MZ sind :
    Result := (Chr(FileData[0]) + Chr(FileData[1])) = 'MZ'


    3. Ermitteln von PE_Offset
    P.PE_Offset := PCardinal( @FileData[$3C] )^;

    Der Offset von PE_Offset im Dos_Header ist $3C = 60 dezimal
    Dort lesen wir halt 4 Bytes (PE_Offset = DWord) ein
    Ach übrigens - P = TParameters


    TParameters = packed record
    {header:}
    PE_Offset : Cardinal; // at $3C in the dos_header
    EntryPoint : Cardinal; // offset to org.ep
    ImageBase : Cardinal;
    SecAlign : Cardinal;
    {section:}
    Sections : Word; // cnt of sections
    CodeOffset : Cardinal; // offset to code (.text) : .. and his values:
    VirtualSize : Cardinal;
    VirtualAddr : Cardinal;
    RawSize : Cardinal;
    RawAddr : Cardinal;
    SectionCave : Cardinal;
    end;


    4. Handelt es sich um einen validen PE-Header ?
    Result := chr( FileData[PEOffset] ) + chr( FileData[PEOffset+1] ) = 'PE';


    5. Hole ImageBase
    P.ImageBase := PCardinal( @FileData[P.PE_Offset+$34] )^;


    6. Der Einsprungspunkt wird hier eingelesen
    P.EntryPoint := PCardinal( @FileData[P.PE_Offset+$28] )^;



    7. Ermittle die Anzahl der Sections
    P.Sections := PWord( @FileData[P.PE_Offset+6] )^;


    8. Offset von ".text" - Section

    // ÃœbergabeParameter: FileData: TByteArr; SectionName: TObject_Name; var P: Parameters
    // -- nur so nebenbei: TObject_Names = Array[0..7] of Char
    var
    Section: TSection_Table;
    x: Word;
    begin
    P.CodeOffset := P.PE_Offset + $D0; // 48
    x := 0;
    repeat
    inc( P.CodeOffset, SizeOf( TSection_Table ) );
    Section := PSection_Table( @FileData[P.CodeOffset] )^;
    inc(x);
    until (Section.Object_Name = SectionName) or (x > P.Sections);
    if P.CodeOffset = P.PE_Offset + $D0 then
    P.CodeOffset := 0;

    Hier werden die Sections eingelesen und es wird geprüft ob es sich um die
    gesuchte Section handelt ! Dies wird solange gemacht, bis man alle Sections durch hat.
    Falls nichts gefunden werden konnte, wird 0 zurückgegeben !

    9. Ermittle einzelne Werte dieser Section:
    // Ãœbergabe Parameter = FileData: TByteArr; var P: TParameters
    with PSection_Table( @FileData[P.CodeOffset] )^ do
    with P do
    begin
    P.VirtualSize := Virtual_Size;
    P.VirtualAddr := RVA;
    P.RawSize := Physical_Size;
    P.RawAddr := Physical_Offset;
    end;


    10. Cave ermitteln:
    Die Größe des Caves kann man wie folgt ermitteln:
    RawSize - VirtualSize // RawSize = Physical_Size

    11. Crypten der Section
    // Ãœbergabe Parameter = var FileData: TByteArr; SectionStart, SectionEnd: Cardinal;
    // Magic: Byte
    // Magic wird für die Xor-Verschlüsselung verwendet !
    var
    i: Integer;
    begin
    for i := SecStart to SecEnd do
    FileData[i] := FileData[i] xor Magic;


    12. ASM-Code erstellen und anpassen:
    Zuerst einmal erstellen wir einen Container.. ja so nenne ich das - "Container"
    Diesen Container befüllen wir nachher mit den gewünschten Adressen und zwar:
    SectionStart
    SectionEnd
    OrginalEntryPoint

    mov eax, .codestart // eax := CodeStart ...
    mov edx, .codeend
    mov ecx, orginal entrypoint
    // mainloop start:
    xor ss[eax], magic // Eax^ := Eax^ xor magic
    inc eax // inc( eax )
    cmp eax, edx // if not CodeStart = CodeEnd then
    jne mainloop // repeat ... springe zurück zu mainloop
    // jmp to entrypoint // else
    jmp ecx // springe zum EntryPoint

    Man kann die einzelnen Bytes durch das Debuggen ermitteln:


    $B8, $00, $00, $00, $00,
    $BA, $00, $00, $00, $00,
    $B9, $00, $00, $00, $00,
    // mainloop start:
    $36, $83, $30, $00,
    $40,
    $39, $D0,
    $75, $F7,
    // jmp to entrypoint
    $FF, $E1 );

    Ich habe ein Array mit diesen Werten befüllt und ein Paar Index-Konstanten deklariert, damit ich später leichter darauf zugreifen kann !
    Wie ihr sehen in den ersten 3 Zeilen unseres Code jede Menge Nullen.
    Diese werden wie schon erwähnt mit den Adressen befüllt !

    Unserer Container-Ersteller Procedure/whatever übergeben wir folgende Parameter:
    CodeStart: ImageBase + (Section)VirtualAddr
    CodeEnd: ImageBase + (Section)VirtualAddr + (Section)VirtualAddr
    EntryPoint: ImageBase + EntryPoint
    Magic: Magic ! Dieser Wert muss = Wert, mit dem ihr die Section gecrypted habt, sein !

    13. Den "Container" in die Datei einfügen
    // Ãœbergabe Parameter: Offset zur Ende der Section
    Move( Stub[0], FileData[Offset], Length(Stub) );

    Offset = RawAddr+VirtualSize !!

    14. Den neuen Einsprungspunkt setzen und Werte anpassen:
    Dazu habe ich mir eine Hilfsfunction Namens "Parameters" geschrieben,
    die mir eine TParameters-Struktur mit den übergegebenen Werten
    zurück liefert;
    Diese Procedure SetSectionParams setzt die Parameter und
    was noch wichtig ist:
    Es setzt die Flags auf writeable / executable !
    Object_Flags := SECTION_WRITEABLE_EXECUTEABLE;
    SetSectionParams( FileData, Params,
    Parameters(PE_Offset, EntryPoint, ImageBase,
    SecAlign, Sections, CodeOffset,
    VirtualSize + Length(Stub), VirtualAddr, RawSize,
    RawAddr, SectionCave) );

    den neuen EntryPoint setzen:
    // Ãœbergabe Parameter: var FileData: TByteArr; P: Parameters; NexEntryP: Cardinal
    PCardinal( @FileData[P.PE_Offset+$28] )^ := NewEntryP;

    NewEntryP = VirtualAddr+VirtualSize


    Ps: Diesen Thread habe ich aus einem anderem Forum herauskopiert für euch. Ich hoffe das er euch weitergeholfen hat

    mfg Marco

    Edit: Sry. hab noch vergessen woher ich es habe
    http://www.delphipraxis.net/post1003799.html
    Geändert von MarcoPeterino (27.02.2009 um 19:57 Uhr)

  2. #2
    0x4D5A5045 Avatar von snify
    Registriert seit
    23.12.2008
    Beiträge
    215

    Standard

    Wow bin zwar gerade am Delphi lernen.
    Aber ich muss schon sagen, dass du dir ziemlich Mühe gegeben hast.
    Ich kanns gut gebrauchen, danke dir.
    NEU *USB SPREADING*
    NEU localsteam *fixed* [stealt alle Usernamen+PW]
    YEAAAH ICH BIN BETA TESTER VON APOCALYPSE RAT
    *NEU ACCOUNT EXPANDER PRIVATE* Infos via PM
    www.snify.6x.to ---> wieder online

  3. #3
    Gesperrt
    Registriert seit
    28.01.2009
    Beiträge
    757

    Standard

    Haha, wens rauskopiert is, dann gib bitte auch ne quelle an!
    Du bricht das Copyright

  4. #4
    Stanley Jobson Avatar von otto
    Registriert seit
    29.12.2006
    Beiträge
    694

    Standard

    Zitat Zitat von MarcoPeterino Beitrag anzeigen
    Ps: Diesen Thread habe ich aus einem anderem Forum herauskopiert für euch. Ich hoffe das er euch weitergeholfen hat
    Zitat Zitat von snify Beitrag anzeigen
    Wow bin zwar gerade am Delphi lernen.
    Aber ich muss schon sagen, dass du dir ziemlich Mühe gegeben hast.
    Ich kanns gut gebrauchen, danke dir.
    Mhm,
    ist sehr nett ja klar,
    aber soviel mühe macht copy & paste nich xDD
    @ snify, er hat nur copy & paste.

    Trozdem vielen dank'.
    Früher mit meinen Delphi zeiten hät' ich das gebrauchen können.
    Jaja das waren noch zeiten,
    mit projektpinkt usw
    Just Failed :>


  5. #5
    Superman Avatar von MarcoPeterino
    Registriert seit
    27.10.2008
    Beiträge
    439

    Standard

    Quelle wurde hinzugefügt.

    Danke Hack & Crack

  6. #6
    Swaggy Dude Avatar von mbeezy
    Registriert seit
    28.03.2007
    Beiträge
    2.112

    Standard

    Ich übernehm das mit der Quelle mal: http://www.delphipraxis.net/post1003799.html ;)


    /Edit: Doh, um 1 Minute zu spät. ^^
    #ichwurdezurückgehaltendamals #sheesh #burrr #scurrr #nohomo #turnup #eaglegang #byrdcall #glogangornogang #duschkabinenposse #codeincobracrew

  7. #7
    Gesperrt
    Registriert seit
    28.01.2009
    Beiträge
    757

    Standard

    Bitte, aber denk immer dran!
    Das ist verboten im Forum denke ich mal keine quelle beim geklauten text anzugeben!!!

    Aber trotzdem, Danke

Stichworte

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •