AssembleIt incl. Laufzeit-"Debugger" [Update12/04/2012]

  • Hallo zusammen,

    AssembleIt ist ja schon ein guter Weg, Assemblercode in AutoIt zu integrieren. Allerdings besteht immer das Handicap, dass der Assemblercode nicht so funktioniert wie er soll.
    Welcher Wert steht in welchem Register, wie siehts zzt auf dem Stack aus, wie ist der aktuelle Zustand der Prozessorflags, warum stürzt der Code ab?
    Die Antwort auf diese Fragen war bisher nur durch Klimmzüge herauszufinden, daher habe ich mir gedacht, schreib mal schnell einen Debugger, der diese Daten während der Laufzeit des Programms anzeigt.....
    autoit.de/wcf/attachment/12907/

    Dazu war nötig, aus dem Assemblerscript während der Laufzeit in eine AutoIt-Funktion zu springen und die Daten im DEBUG-Fenster anzuzeigen, also das Script dort "anzuhalten". Soweit nicht sonderlich schwer, allerdings ergab sich das Problem, dass das in der Callbackfunktion angezeigte DEBUG-Fenster wohl Probleme mit dem Messagehandler von Windows hatte. Wenn man versucht, per GuiGetMsg()- oder Endlos-Schleife Nachrichten zu bekommen, erscheint die gefürchtete "Keine Rückmeldung"-Nachricht im Fenstertitel.
    Dies konnte umgangen werden, indem ein Fenster mit eigenem Messagehandler, ein sog. "modales" Fenster angezeigt wurde, z.B. eine Messagebox. Erst nach schliessen dieses modalen Fensters läuft das Script und somit der Assemblercode weiter. Dank progandys Hilfe gelang es, dieses modale Fenster als Button auszubilden (ein Control wird Windowsintern wie ein Fenster behandelt). Danke dafür!

    Daher "hängt" der "NEXT...."-Button so seltsam unter dem Fenster....
    Apropos, um die Debug-GUI zu schliessen d.h. das ASM-Programm läuft dann ohne Stop bis zum Ende, RECHTSKLICK auf den Next-Button, das ist nicht schön, aber selten....
    /EDIT/ progandy hat freundlicherweise eine "3-Button-Version" geschrieben (siehe Bild oben), damit lässt sich der Debugger während der Runtime des Assemblercodes ausschalten (End Debugging) und auch der gesamte Prozess schliessen (Kill)


    Anwendungs Beispiel:
    Um die Debug-Gui anzuzeigen reicht es, mitten im Assemblercode die Zeile

    [autoit]

    _ASMDBG_()

    [/autoit]


    einzufügen. Dann hält das Assemblerprogramm an dieser Stelle an und zeigt die Debug-Gui. Mit Linksklick auf den "Next..."-Button oder Betätigen der "Enter"-Taste gehts dann weiter bis zum nächsten _ASMDBG_()

    Als Goodie kann man noch einen Parameter mitgeben, zzt nur die 32-Bit-Register $edi, $esi, $ebp, $esp, $ebx, $edx, $ecx, $eax, und diese mit einer beliebigen Abbruchbedingung versehen!
    Bei Einfügen von z.B.

    [autoit]

    _ASMDBG_("$eax=22")

    [/autoit]

    in einer Schleife läuft der Assemblercode so lange, bis das EAX-Register den Wert 22 enthält und zeigt erst dann das Debug-Fenster an!

    [autoit]

    _ASMDBG_("$edx>35662")

    [/autoit]

    lässt das Debugfenster erst erscheinen, wenn das EDX-Register grösser ist als 35662.
    Alles was innerhalb der Anführungsstriche steht wird als logischer Ausdruck ausgewertet und bei TRUE wird das Debug-Fenster angezeigt!

    Bekannte Einschränkungen:

    • Die Zeile _("org " & FasmGetBasePtr($Fasm)) ist im ASM-Code erforderlich, sie veranlasst den Assembler den Bezug zu den Adressen richtigzustellen
    • DllCallbackFree($_DBG_) sollte am Ende des Scripts aufgerufen werden
    • /EDIT 07/04/2011 gelöst durch progandy  Ab und zu gibts einen Fehler, z.B. wird hin und wieder das vorderste Fenster beim Debuggen hinter/unter den NEXT...-Button verkleinert, lässt sich dann aber problemlos wieder vergrössern.
    • Bei Verwendung des Debuggers innerhalb einer LOOP-Schleife (im Assemblercode) kommt es zu einer Fehlermeldung, da LOOP nur maximal +-127 Bytes weit springt, aber der intern abgehandelte Debuggercode wesentlich grösser ist. Das kann man umgehen, indem man den "LOOP Sprungadresse" durch

      Code
      DEC ECX 
      CMP ECX,0
      JNE Sprungadresse

      ersetzt und nach dem debuggen wieder durch "LOOP Sprungadresse" ersetzt. Ggf. baue ich diese Umwandlung sogar in den Debugger ein....


    Naja, ganz fertig ist der Debugger noch nicht (welches Programm wird je fertig :whistling: ), der Code ist total chaotisch, aber ich habe Hoffnung, dass es weitergeht.
    Eins noch zum Schluss, ich persönlich finde die GUI echt hässlich, wer da meint, die Aufteilung /Ansicht besser gestalten zu können ist herzlich eingeladen!!!


    /EDIT/ Debuggerupdate 12.05.2012

    AssembleIt incl. Debugger und Testdatei:
    autoit.de/wcf/attachment/15678/

  • Zitat

    die .bin-Datei sollte aber noch gelöscht werden

    wenn man wollte, könnte man den darin enthaltenen Programmcode "normal" mit einem Debugger (wie z.B. IDA Pro free) debuggen oder in andere Programme "einfügen" *husthusthust*, oder sogar als lauffähige *.com-Datei umbenennen...

  • Es ist kein Problem, weitere Buttons auf der modalen Dialog-GUI unterzubringen ;) Das Beispiel hat zwar nur einen, kann aber ziemlich einfach um einen zweiten erweitert werden.
    Edit: Ich hab mal 2 weitere Buttons erstellt und die Funktion etwas optimiert, ich hoffe dass das in Ordnung geht ;)

    Spoiler anzeigen
    [autoit]

    ...

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

    ;debug-funktion, aus dem asmcode per call an die callback-adresse aufgerufen
    Func _DBG_MSGBOX($anz, $edi, $esi, $ebp, $esp, $ebx, $edx, $ecx, $eax);aus asm übergebene register
    If $_DBG_noshowflag = 1 Then Return 0
    GUISetState(@SW_SHOW, $_DBG_GUI)
    _WinAPI_UpdateWindow($_DBG_GUI)
    ;_DBG_WM_SIZE($_DBG_GUI,0,0,0)

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

    Dim $reg[8] = [$eax, $ebx, $ecx, $edx, $esi, $edi, $esp, $ebp]
    _GUICtrlListView_BeginUpdate($Listview_reg32)

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

    For $i = 0 To 7 ;fenster mit werten füllen
    GUICtrlSetData($listviewitem_reg32[$i], "|" & Ptr($reg[$i]) & "|" & _
    String($reg[$i]) & "|" & _
    StringFormat(" %2.6G", int2float($reg[$i])) & "|" & int2bin($reg[$i])) ;hex
    GUICtrlSetData($_DBG_LABEL[$i + 80], DllStructGetData($struct_double, 1, $i + 1));FPU st0-st7
    $struct = DllStructCreate("byte[16]", $ptr_SSE + 16 * $i)
    GUICtrlSetData($_DBG_LABEL[$i + 90], DllStructGetData($struct, 1))
    GUICtrlSetData($_DBG_LABEL[$i + 100], BitAND(2 ^ $i, DllStructGetData($struct_EFLAGS, 1)) / (2 ^ $i));eflags

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

    $struct = DllStructCreate("double[2]", $ptr_SSE + 16 * $i); 2x 64byte DOUBLE SSE
    GUICtrlSetData($_DBG_LABEL[$i + 110], StringFormat("%6s %6s", DllStructGetData($struct, 1, 1), DllStructGetData($struct, 1, 2)))
    $struct = DllStructCreate("float[4]", $ptr_SSE + 16 * $i); 4x 32byte FLOAT SSE
    GUICtrlSetData($_DBG_LABEL[$i + 120], StringFormat("%10.5f %10.5f %10.5f %10.5f", DllStructGetData($struct, 1, 1), DllStructGetData($struct, 1, 2), DllStructGetData($struct, 1, 4), DllStructGetData($struct, 1, 4)))
    Next
    GUICtrlSetData($_DBG_LABEL[101], BitAND(2 ^ 10, DllStructGetData($struct_EFLAGS, 1)) / (2 ^ 10));eflags DF
    GUICtrlSetData($_DBG_LABEL[103], BitAND(2 ^ 11, DllStructGetData($struct_EFLAGS, 1)) / (2 ^ 11));eflags OF
    For $i = 0 To 10 ;stack
    GUICtrlSetData($_DBG_LABEL[141 + $i], Ptr(DllStructGetData($struct_STACK, 1, $i + 1)));stack hex
    GUICtrlSetData($_DBG_LABEL[155 + $i], Int(DllStructGetData($struct_STACK, 1, $i + 1)));stack int
    Next
    _GUICtrlListView_EndUpdate($Listview_reg32)

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

    If $anz = 0 Or Execute($_dbg_string[$anz]) Then
    Switch __GET_MSGBOX_BUTTON()
    Case 0, 1
    Case 2
    $_DBG_noshowflag = True
    Case 3
    DllCall("kernel32.dll", "none", "ExitProcess", "int", 0xDEADBEEF)
    EndSwitch
    EndIf
    Return 0
    EndFunc ;==>_DBG_MSGBOX
    Func __GET_MSGBOX_BUTTON()
    Local Static $tDLG, $aOldPos[4] = [0, 0, -1, -1]
    $pos_DBB_Window = WinGetPos($_DBG_GUI) ;Positionsdaten der GUI holen
    If $aOldPos[0] <> $pos_DBB_Window[0] Or $aOldPos[1] <> $pos_DBB_Window[1] Or $aOldPos[2] <> $pos_DBB_Window[2] Or $aOldPos[2] <> $pos_DBB_Window[2] Then
    $aOldPos = $pos_DBB_Window
    Local $x = Int($pos_DBB_Window[0] / 2)
    Local $y = Int(($pos_DBB_Window[1] + $pos_DBB_Window[3]) / 2)

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

    ;es folgen die Daten für den "...NEXT"-Button, der muss ein modales Fenster sein, wer da eine andere Idee hat, bitte melden
    Local $w = Int($pos_DBB_Window[2] / 2) ;breite Button
    Local $h = 30 ;höhe Button
    $bTitle = StringToBinary("Next....", 2) ;text Button
    $bXY = BinaryMid(Binary($x), 1, 2) & BinaryMid(Binary($y), 1, 2);Buttondaten
    $bWH = BinaryMid(Binary($w), 1, 2) & BinaryMid(Binary($h), 1, 2)
    $bWhalfH = BinaryMid(Binary(Int($w / 2 - 15)), 1, 2) & BinaryMid(Binary($h), 1, 2)
    $bDIALOG = Binary("0x00000090400000040300") & $bXY & $bWH & Binary("0x000000000000") & Binary("0x000000500000000000000000") & $bWhalfH & Binary("0x0100FFFF8000") & $bTitle & Binary("0x0000") & Binary("0x0000")
    ; |Style ||ExStyl||cdit| |Empty "Arrays"| |Style ||ExStyl||x ||y | |id||BUTTON| |Chr0| |irgend welche anderen Arrays
    $x = Mod(BinaryLen($bDIALOG), 4)
    If $x Then $bDIALOG &= BinaryMid(Binary("0x000000"), 1, $x)
    $bTitle = StringToBinary("End Debugging", 2) ;text Button
    $bDIALOG &= Binary("0x0000005000000000") & BinaryMid(Int($w / 2 - 15), 1, 2) & Binary("0x0000") & $bWhalfH & Binary("0x0200FFFF8000") & $bTitle & Binary("0x0000") & Binary("0x0000")

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

    $x = Mod(BinaryLen($bDIALOG), 4)
    If $x Then $bDIALOG &= BinaryMid(Binary("0x000000"), 1, $x)
    $bTitle = StringToBinary("Kill", 2) ;text Button
    $bDIALOG &= Binary("0x0000005000000000") & BinaryMid(Int($w - 30), 1, 2) & Binary("0x0000") & BinaryMid(30, 1, 2) & BinaryMid($h, 1, 2) & Binary("0x0300FFFF8000") & $bTitle & Binary("0x0000") & Binary("0x0000")

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

    $tDLG = DllStructCreate("byte[" & BinaryLen($bDIALOG) & "]")
    DllStructSetData($tDLG, 1, $bDIALOG) ;Button-Daten in struct schreiben
    EndIf
    Local $aRet = DllCall("user32.dll", "int", "DialogBoxIndirectParamW", "ptr", 0, "ptr", DllStructGetPtr($tDLG), "hwnd", 0, "ptr", DllCallbackGetPtr($dlgproc), "lparam", 0)
    If @error Then Return 0
    Return $aRet[0]
    EndFunc ;==>__GET_MSGBOX_BUTTON

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

    Func _DlgProc($hwnd, $uMsg, $wParam, $lParam)
    ;thx to progandy!
    If $_DBG_firstcall Then
    $_DBG_buttonID = $wParam
    $_DBG_firstcall = False
    EndIf
    If $hwnd <> $_DBG_GUI Then $hwnd_weiterbutton = $hwnd
    If $uMsg = $WM_CLOSE Then
    DllCall("user32.dll", "bool", "EndDialog", "hwnd", $hwnd, "int_ptr", 0)
    Return True
    ElseIf $uMsg = $WM_COMMAND Then
    DllCall("user32.dll", "bool", "EndDialog", "hwnd", $hwnd, "int_ptr", BitAND($wParam, 0xFFFF))
    Return True
    EndIf
    Return False
    EndFunc ;==>_DlgProc
    ...

    [/autoit]

    Edit: Ein paar Erklärungen. Der Button mit ID 1 ist Next, der Button mit ID 2 ist Fortsetzen ohne Debuggen ("End Debugging"), Button ID 3 ("Kill") ist zum sofortigen abbrechen per ExitProcess. Damit wird das Skript direkt abgeschossen.

    2 Mal editiert, zuletzt von progandy (11. März 2011 um 23:30)

  • Zitat von progandy

    Edit: Ich hab mal 2 weitere Buttons erstellt und die Funktion etwas optimiert, ich hoffe dass das in Ordnung geht

    Und wie das in Ordnung geht! :thumbup:

    Zitat von sprenger120

    Ich persönlich finde das die GUI eigentlich ganz i.o. ist. Es soll ja ein Debugger sein und kein Kanidat für Germany's Next Topmodel


    eigentlich braucht man doch idR nur die Register und die Flags, ggf noch den Stack, da kann die "normale" GUI recht klein und übersichtlich bleiben.
    Um die Register der FPU und /oder XMM darzustellen, könnte man entweder weitere Buttons anhängen, oder bspw. per Parameter in der _ASMDBG_("-XMM") oder _ASMDBG_("-FPU") einschalten.
    Den XMM-Teil würde ich gerne sowieso noch grösser machen, weiterhin könnte man dann auch per _ASMDBG_("-MEM0xABCDEFAB") einen Speicherbereich darstellen.

  • Hi,
    minimalst Bug gefixt (oder hätte ich BugFix schreiben sollen^^) in der Ausgabe der SSE-Register, dort wurde Aufgrund eines c&p-Fehlers das letzte float nicht richtig dargestellt....
    Download s. erster Post

  • Hi Andy,
    ich habe einen sehr nervigen Bug gefunden. Wenn ich AssembleIt.au3 includet habe und dann _ArrayDisplay aufrufe wird mein SciTE Fenster und das Fenster von _ArrayDisplay zusammengequetscht.

    autoit.de/wcf/attachment/13037/

    Testcode
    [autoit]

    #AutoIt3Wrapper_usex64=n
    #include "AssembleIt.au3"
    #include <Array.au3>

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

    Dim $aArray[3] = [1,2,3]

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

    _ArrayDisplay($aArray)

    [/autoit]


    Das _ArrayDisplay lässt sich danach auch nicht mehr auseinander ziehen weil es immer wieder auf die vorherige Größe zurückgesetzt wird.
    Ich hoffe das du den Bug fixen kannst, weil der schon drinne ist seid ich die Closed Beta bekommen habe.

  • Das gleiche mit normalen GUIs, mir ist aber nicht aufgefallen, dass es an Assembleit liegt ^^ Es wird an der DialogProc liergen, ich schätze du musst das HWND des Dialogs anders herausfinden


    Ich würde das so machen:

    Spoiler anzeigen
    [autoit]

    Global $_DBG_BUTTONSGUI = -1
    GUIRegisterMsg(0x0216, "_DBG_WM_MOVING") ; $WM_MOVING
    GUIRegisterMsg(0x0232, "_DBG_WM_MOVING") ; $WM_EXITSIZEMOVE
    GUIRegisterMsg($WM_MOVE, "_DBG_WM_MOVING")

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

    ...

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

    Func _DlgProc($hwnd, $uMsg, $wParam, $lParam)
    ;thx to progandy!
    If $uMsg = $WM_INITDIALOG Then
    $_DBG_BUTTONSGUI = $hwnd
    ElseIf $uMsg = $WM_CLOSE Then
    DllCall("user32.dll", "bool", "EndDialog", "hwnd", $hwnd, "int_ptr", 0)
    Return True
    ElseIf $uMsg = $WM_COMMAND Then
    DllCall("user32.dll", "bool", "EndDialog", "hwnd", $hwnd, "int_ptr", BitAND($wParam, 0xFFFF))
    Return True
    ElseIf $uMsg = $WM_DESTROY Then
    $_DBG_BUTTONSGUI = -1
    EndIf
    Return False
    EndFunc ;==>_DlgProc
    Func _DBG_WM_MOVING($hWnd, $uMsg, $wParam, $lParam)
    If $hwnd = $_DBG_GUI And IsHWnd($_DBG_BUTTONSGUI) Then
    Local $pos = WinGetPos($hwnd)
    WinMove($_DBG_BUTTONSGUI, "", $pos[0], $pos[1]+$pos[3])
    EndIf
    Return $GUI_RUNDEFMSG
    EndFunc

    [/autoit]
  • ja, das verkleinern der gerade den Fokus habenden GUI auf die Größe der 3 Buttons war schon immer so^^
    Bei Programmen mit GUI war das nicht besonders schlimm, da dann meist Scite "verkleinert" wurde, aber das konnte man am Rahmen wieder vergrössern.
    Bei Programmen ohne GUI bzw. mehreren Gui´s ist das echt nervig, besonders wenn Fenster betroffen sind, die keinen größenveränderlichen Rahmen haben....

    Von daher, vielen Dank an progandy für die schnelle Hilfe!

  • Update 12/05/2012
    Debugger zeigt nun SSE-Register "richtig" an, bitweise von rechts nach links
    neue Beispieldatei mit Erklärung von _ASSEMBLEIT_FLAG