FASM Basepointer Problem

  • Servus,
    aus irgendeinem Grund steht in meinem ESI-Register nicht die Startadresse des Programms; das Programm stürzt bei folgendem Code ab:

    Spoiler anzeigen
    [autoit]

    $FasmObj = FasmInit()
    FasmReset($FasmObj)
    FasmAdd($FasmObj, "use32")
    FasmAdd($FasmObj, "mov eax, dword[esi+var1]")
    FasmAdd($FasmObj, "ret")
    FasmAdd($FasmObj, "var1 db 16")
    $Bytecode=FasmGetBinary($FasmObj)
    $tCodebuffer=DllStructCreate("byte["&stringlen($Bytecode)/2-1&"]")
    DllStructSetData($tCodeBuffer,1,$Bytecode)
    $Ret= DllCall("user32.dll", "int", "CallWindowProcW", "ptr", DllStructGetPtr($tCodeBuffer, ""), "int", 1, "int", 1, "int", 0, "int", 0)
    _ArrayDisplay($Ret)

    [/autoit]


    Übergeb ich den Basepointer manuell über ein Parameter, dann klappts natürlich:

    Spoiler anzeigen
    [autoit]

    #include <FASM.au3>
    #include <Array.au3>

    [/autoit] [autoit][/autoit] [autoit]

    $FasmObj = FasmInit()
    FasmReset($FasmObj)
    FasmAdd($FasmObj, "use32")
    FasmAdd($FasmObj, "mov eax, dword[esp+4]")
    FasmAdd($FasmObj, "mov eax, dword[eax+var1]")
    FasmAdd($FasmObj, "ret")
    FasmAdd($FasmObj, "var1 db 16")
    $esi = FasmGetBasePtr($FasmObj)
    $Bytecode=FasmGetBinary($FasmObj)
    $tCodebuffer=DllStructCreate("byte[" & StringLen($Bytecode)/2-1&"]")
    DllStructSetData($tCodeBuffer,1,$Bytecode)
    $Ret= DllCall("user32.dll", "int", "CallWindowProcW", "ptr", DllStructGetPtr($tCodeBuffer, ""), "int", $esi, "int", 1, "int", 0, "int", 0)
    _ArrayDisplay($Ret)

    [/autoit]


    Ich würd eben gerne nur mit dem Bytecode dann arbeiten, deshalb kommt dann der Befehl FasmGetBasePtr() nicht mehr in Frage...

    Ich frag mich natürlich was mit meinem ESI-Register los ist, und wenns nicht anders geht, wie ich an den Basepointer ran komm.
    Ich hoff ich konnte mich verständlich ausdrücken:)

    Zitat

    You just keep on trying 'till you run out of cake. ;)


    [STEAM] Source UDF

    Einmal editiert, zuletzt von K4z (14. Februar 2012 um 14:23)

  • Hi,

    ich kenne mich nicht mit der FASM.au3 aus, aber warum sollte in dem ersten Script der Base pointer in ESI befinden ?
    Der Base pointer ist normalerweise in EBP/RBP abgelegt. Innerhalb von Funktionen wird dieses Register aber zweckentfremdet und für den Zugriff auf Parameter und lokale Variablen auf dem Stack benutzt.

    Du wirst, Wohl oder Übel, nicht um den Aufruf von FasmGetBasePtr() herumkommen.
    Ansonsten sieh Dir an was die Funktion macht und mache es dann selber.
    Aber dann kannst Du auch gleich FasmGetBasePtr() benutzen.


    Gruß
    Greenhorn


  • Ich frag mich auch warum er sich in ESI befinden sollte, aber Andy hat in seinem FASM Tutorial folgendes geschrieben:

    Zitat

    Aber um nicht einen der 4 wertvollen der Übergabe-Parameter mit dem Pointer zu "verschwenden" habe ich ein wenig gestöbert und herausgefunden, daß beim Starten unseres Bytecodes die Startadresse im ESI-Register steht! Ob AutoIt oder Windows dafür verantwortlich sind, ist mir ehrlich gesagt egal


    Scheinbar ist es zumindest bei mir nicht so...
    Ich schau mich nochma bissle um und schreib wenn ich ne Lösung gefunden hab.

  • Hmm,

    übrigens braucht man die Funktion CallWindowProc nicht mehr.
    AutoIt 3.3.8 beinhaltet eine neue Funktion DllCallAddress.
    Damit sollte alles ein wenig runder laufen.
    Evtl. muss die FASM.au3 ewas angepasst werden, aber wie gesagt, ich kenne mich mit der UDF nicht aus.
    Am Besten einfach mal Andy direkt fragen. ;)


    Gruß
    Greenhorn


  • Hi,

    Zitat

    übrigens braucht man die Funktion CallWindowProc nicht mehr.
    AutoIt 3.3.8 beinhaltet eine neue Funktion DllCallAddress.

    uuups, danke für den Hinweis^^, man kann nun auch mehr als 4 Parameter übergeben.

    K4z,
    das mit dem Basepointer in ESI war eine zufällige Fundsache (XP32). Besser ist es natürlich, den BP direkt zu übergeben.
    Wie wäre es, beim erstellen der Struct für den Bytecode die Adresse zu merken und direkt in den Code zu patchen?

  • Hi,

    uuups, danke für den Hinweis^^, man kann nun auch mehr als 4 Parameter übergeben


    Ich glaube das ging sogar schon vorher mit DllCall.
    Im englischen Forum habe ich vor kurzem einen Code-Ausschnitt von ProgAndy gesehen, in dem er DllCall ohne Angabe einer DLL und der Adresse aufgerufen hatte. Leider weiß ich nicht mehr wo genau ...


    Gruß
    Greenhorn


  • Hi,
    K4z
    das mit dem Basepointer scheint wohl Versionsabhängig zu sein...

    [autoit]

    #include "assembleit.au3"
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : FasmGetBasePtr($Fasm) = ' & FasmGetBasePtr($Fasm) & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console

    [/autoit][autoit][/autoit][autoit]

    _assembleit("ptr", "_asm")

    [/autoit][autoit][/autoit][autoit]

    DllCallbackFree($_DBG_)

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    Func _asm()
    _("use32")
    _("org " & FasmGetBasePtr($Fasm))
    _asmdbg_()
    _("ret")
    EndFunc ;==>_asm

    [/autoit]


    führt in der Console zu

    Zitat

    @@ Debug(2) : FasmGetBasePtr($Fasm) = 0x026AE014

    vgl EAX-Registerautoit.de/wcf/attachment/15091/


    /EDIT/
    oder einfacher

    [autoit]

    #include "assembleit.au3"
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : FasmGetBasePtr($Fasm) = ' & FasmGetBasePtr($Fasm) & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console

    [/autoit][autoit][/autoit][autoit]

    $a=_assembleit("ptr", "_asm")
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $a = ' & $a & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console

    [/autoit][autoit][/autoit][autoit]

    DllCallbackFree($_DBG_)

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    Func _asm()
    _("use32")
    _("ret")
    EndFunc ;==>_asm

    [/autoit]
  • Zitat von Andy irgendwann mal in seinem ASM Thread...

    Ich habe mich bei der Zusammenarbeit FASM/AutoIt für CallWindowProcW entschieden. Das eigentliche Assemblerprogramm bleibt identisch, aber da CallWindowProcW den Stack "aufräumt" muss man nur noch mit einem einfachen RET den Assemblercode abschliessen.

    Wenn man jetzt DllCallAdress nutzt stattdessen, "räumt" das den Stack auch auf, oder muss man dann aufpassen, dass man ihn selbst am Ende wieder leert ?
    (Ich weiß leider nicht wie man das rausbekommen kann, sonst hätte ich es schon getan^^)
    Dann müsste man nur noch die Geschwindigkeit im vergleich zu CallWindowProc testen. Ob sich der Kram überhaupt für kleine Funktionen lohnt.

    lg
    M

  • @Marsi,
    von der Geschwindigkeit her wird sich nichts ändern, im Prinzip wird das Programm in den speicher geladen und dann per call gestartet, wie bei dll´s auch.

    Zitat

    Wenn man jetzt DllCallAdress nutzt stattdessen, "räumt" das den Stack auch auf, oder muss man dann aufpassen, dass man ihn selbst am Ende wieder leert ?
    (Ich weiß leider nicht wie man das rausbekommen kann, sonst hätte ich es schon getan)

    probier doch einfach mal DllCallAddress aus:

    [autoit]

    #include "assembleit.au3"
    #include "array.au3"
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : FasmGetBasePtr($Fasm) = ' & FasmGetBasePtr($Fasm) & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console

    [/autoit][autoit][/autoit][autoit]

    ;~ $_assembleit_flag=0

    [/autoit][autoit][/autoit][autoit]

    ;~ $a=_assembleit("ptr", "_asm")
    ;~ ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $a = ' & $a & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console

    [/autoit][autoit][/autoit][autoit]

    Global $tCodeBuffer = DllStructCreate("byte[7]") ;reserve Memory for opcodes
    DllStructSetData($tCodeBuffer, 1,"0x8B442404C20400") ;write opcodes into memory

    [/autoit][autoit][/autoit][autoit]

    $ret=DllCalladdress( "ptr", DllStructGetPtr($tCodeBuffer),"int",0x64);,"int",0,"int",0,"int",0)
    _arraydisplay($ret)

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    DllCallbackFree($_DBG_)

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    Func _asm()
    _("use32")
    _("mov eax,[esp+4]")
    _("ret 4")
    EndFunc ;==>_asm

    [/autoit]


    Stack muss man also selbst aufräumen^^

  • @K4v,
    welche Windows-Version?
    Habe leider diese Woche nicht mehr die Möglichkeit, anderes als XP32 zu testen.


  • Stack muss man also selbst aufräumen^^

    Nicht unbedingt. DllCallAddress funktioniert wie ein DllCall. Normalerweise wird stdcall verwendet (Routine räumt Stack selber auf). Man kann auch cdecl verwenden (Als ReturnType z.B. "int:cdecl"). Dann wird der Stack vom Aufrufer leergeräumt. Ein Vorteil der cdecl-Methode ist, dass man eine variable Parameteranzahl übergeben kann, wie z.B. bei printf in C.

  • Hi,
    Hab jetzt mal etwas rumexperimentiert, aber die wirklich einfachste Methode ist das Übergeben des (E)IP=Instruction Pointer per Parameter

    Bei Verwendung von AssembleIt (oder Fasm.au3), d.h. beim Erstellen und Testen des ASM-Codes wird

    [autoit]

    FasmGetBasePtr($Fasm)

    [/autoit]

    als Parameter übergeben.

    Funktioniert der ASM-Code, setzt man das

    [autoit]

    $_assembleit_flag=0

    [/autoit]

    .
    Dadurch werden die Codezeilen generiert, welche den CallWinProcW-Call beinhalten.

    [autoit]

    Global $tCodeBuffer = DllStructCreate("byte[26]") ;reserve Memory for opcodes
    DllStructSetData($tCodeBuffer, 1,"0x8B7424048B460E034612034616C30C000000B0000000000A0000") ;write opcodes into memory
    $ret=DllCall("user32.dll", "ptr", "CallWindowProcW", "ptr", DllStructGetPtr($tCodeBuffer),"int",Param1,"int",0,"int",0,"int",0)

    [/autoit]


    Der Parameter Param1 wird nun durch

    [autoit]

    dllstructgetptr($tcodebuffer)

    [/autoit]

    ersetzt.
    Da der Assembler während des Assemblierens die Adressen der Variablen berechnet, als ob ein "ORG 0" vorhanden ist, ergibt die Addition der Variablenadresse und der Startadresse des Programms die richtige Variablenadresse im Speicher, egal an welche Speicheradresse der Code geladen wird.

    Beispiel:

    Spoiler anzeigen
    [autoit]

    #include "assembleit.au3"
    #include "array.au3"

    [/autoit] [autoit][/autoit] [autoit]

    ;~ $_assembleit_flag=0
    ;~ $a=_assembleit("ptr", "_asm","int",FasmGetBasePtr($Fasm))
    ;~ ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $a = ' & $a & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
    ;~ exit

    [/autoit] [autoit][/autoit] [autoit]

    ;von assembleit erstellt, wenn $_assembleit_flag=0
    Global $tCodeBuffer = DllStructCreate("byte[26]") ;reserve Memory for opcodes
    DllStructSetData($tCodeBuffer, 1,"0x8B7424048B460E034612034616C30C000000B0000000000A0000") ;write opcodes into memory

    [/autoit] [autoit][/autoit] [autoit]

    ;Param1 ersetzen durch DllStructGetPtr($tcodebuffer)
    $ret=DllCall("user32.dll", "ptr", "CallWindowProcW", "ptr", DllStructGetPtr($tCodeBuffer),"int",dllstructgetptr($tcodebuffer),"int",0,"int",0,"int",0)
    _arraydisplay($ret)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    DllCallbackFree($_DBG_)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _asm()
    _("use32")
    _("mov esi,[esp+4]")
    _("mov eax,[esi+Var1]") ;00C
    _("add eax,[esi+Var2]") ;0BC
    _("add eax,[esi+Var3]") ;ABC
    _("ret")

    [/autoit] [autoit][/autoit] [autoit]

    _("Var1 dd 0x00C")
    _("Var2 dd 0x0B0")
    _("Var3 dd 0xA00")
    EndFunc ;==>_asm

    [/autoit]
  • Ok, richtig nice:D Klappt perfekt, viellteicht könntest du ja dann gleich dein Tutorial bisschen ergänzen. Jetzt muss man zwar einen zusätzlichen Parameter opfern, aber das macht ja nix, man kann ne dllstruct verwenden.
    Dann bedank ich mich mal für die Hilfe :)

  • Wie wäre es mit so etwas als erstem Befehl, um die erste Adresse ins ESI-register zu bekommen? Das geht aber nur, wenn AutoIt das Register nicht anderweitig belegt hat.

    Code
    call next
    next: pop esi