Seite 1 von 2

Python mit ASM funktioniert nicht.

Verfasst: Samstag 21. Januar 2023, 09:30
von funkheld
Hallo, guten Tag.

Ich suche bitte den Fehler in dieser Demo , weil bei :
r = f(42)
print (r)
der Wert "5" rauskommt oder ein anderer Wert der hier drin steht : b'\x83\xc0\x05'
statt 42+5

Wer kann bitte helfen?
Python 3.... und WIN10

Danke.

Code: Alles auswählen

import ctypes

asm_function = (
    b'\x8b\xc1'      # mov eax, ecx
    b'\x83\xc0\x05'  # add eax, 5
    b'\xc3'          # ret
)

# https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc#MEM_COMMIT
MEM_COMMIT = 0x00001000
# https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc#MEM_RESERVE
MEM_RESERVE = 0x00002000
# https://learn.microsoft.com/en-us/windows/win32/memory/memory-protection-constants#PAGE_EXECUTE_READWRITE
PAGE_EXECUTE_READWRITE = 0x40
# https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc
ctypes.windll.kernel32.VirtualAlloc.argtypes = (
    ctypes.c_void_p,  # LPVOID
    ctypes.c_size_t,  # SIZE_T
    ctypes.c_long,    # DWORD
    ctypes.c_long,    # DWORD
)
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_void_p  # LPVOID

memory_buffer = ctypes.windll.kernel32.VirtualAlloc(
    0,                         # lpAddress - NULL
    len(asm_function),         # dwSize
    MEM_COMMIT | MEM_RESERVE,  # flAllocationType
    PAGE_EXECUTE_READWRITE     # flProtect
)

if not memory_buffer:  # VirtualAlloc returned NULL
    print("VirtualAlloc call failed. Error code:", ctypes.GetLastError())
    exit(-1)

c_buffer = ctypes.c_char_p(asm_function)

# https://learn.microsoft.com/en-us/windows/win32/devnotes/rtlmovememory
ctypes.windll.kernel32.RtlMoveMemory.argtypes = (
    ctypes.c_void_p,  # VOID*
    ctypes.c_void_p,  # VOID*
    ctypes.c_size_t   # SIZE_T
)

ctypes.windll.kernel32.RtlMoveMemory(
    memory_buffer,     # Destination
    c_buffer,          # Source
    len(asm_function)  # Length
)

f = ctypes.cast(
    memory_buffer,
    ctypes.CFUNCTYPE(
        ctypes.c_int,  # return type
        ctypes.c_int   # argument type
    )
)

r = f(42)
print(r)

Re: Python mit ASM funktioniert nicht.

Verfasst: Samstag 21. Januar 2023, 11:01
von __deets__
Warum schreibst du die Funktion nicht in Python? Oder, wenn es assembler sein soll, benutzt eine Sprache wie C++, die das relative einfach integriert.

Re: Python mit ASM funktioniert nicht.

Verfasst: Samstag 21. Januar 2023, 11:45
von funkheld
C++ ist nicht meine Sache . Mit ASM kenne ich mich besser aus.
Bloß das hineinbringen der Variablen mit ctypes macht mir schwierigkeiten.

Danke.

Re: Python mit ASM funktioniert nicht.

Verfasst: Samstag 21. Januar 2023, 11:49
von __deets__
Dann wird deine Neugierde sich auf das debuggen solcher Anwendungen ausdehnen müssen. Denn es wird nie so sein, dass du “einfach” Assembler schreiben können wirst, und den dann ausführen. Was hier kracht ist das CFFI, und das ist komplex, und will umfänglich verstanden sein, wenn man sowas machen will. Schmeiß also WinDBG an, und schau, was da so passiert.

Re: Python mit ASM funktioniert nicht.

Verfasst: Samstag 21. Januar 2023, 12:07
von __deets__
Nachtrag: wenn das Ergebnis immer das immediate-Argument ist, dann muss folgerichtig eax 0 sein. Und warum das 0 und nicht 42 ist, liegt eben am CFFI, was durch ctypes genutzt wird.

Re: Python mit ASM funktioniert nicht.

Verfasst: Samstag 21. Januar 2023, 12:49
von __blackjack__
Mehr als aus Neugier wird man das rein in Assembler ja sowieso nicht machen wollen. Das ist kompliziert und fehleranfällig, funktioniert so dann nur auf einer Plattform, also Windows *und* 32-Bit schränken das ein, und man wird sich der Einfachheit halber sowieso mit C beschäftigen müssen, denn das c in `ctypes` steht ja für C.

Werden bei der Windows, 32-Bit ABI Argumente überhaupt über Register übergeben? Wer muss den Stack aufräumen? Aufrufer oder Aufgerufener?

Re: Python mit ASM funktioniert nicht.

Verfasst: Samstag 21. Januar 2023, 12:54
von __deets__
Gute Frage. Kann man ja mal mit rumspielen, Matt Godbolt sei Dank: https://godbolt.org/z/11G8ava6d

Ich wuerde das erstmal als via Stack uebertragen interpretieren.

Wenn man Assembler machen will, dann ist C oder C++ dafuer schlicht um Groessenordnungen besser geeignet. Weil das Tooling schon gleich dabei ist, ein Debugger dafuer kann immer auch Assembler darstellen, und man sieht ueberhaupt, was da passiert. Python ist da wirklich die schlechtmoeglichste Wahl.

Re: Python mit ASM funktioniert nicht.

Verfasst: Samstag 21. Januar 2023, 13:25
von Sirius3
__deets__: der Stack wird nur für die lokalen Variablen verwendet.
Die optimierte Version sieht so aus:

Code: Alles auswählen

   lea     eax, DWORD PTR [rcx+42]
   ret
AMD64 hat genug Register, dass man die meisten Parameter ohne Stack übergeben kann.
Ich habe kein Windows um das selbst testen zu können, sehe aber keinen offensichtlichen Fehler.

Unter Linux würde das so aussehen

Code: Alles auswählen

import ctypes
import mmap

CODE = b"\x8d\x47\x2a\xc3"

code_mem = mmap.mmap(-1, len(CODE), mmap.MAP_PRIVATE, mmap.PROT_EXEC | mmap.PROT_READ | mmap.PROT_WRITE)
code_mem[:] = CODE

function = ctypes.cast(
    (ctypes.c_char*len(CODE)).from_buffer(code_mem),
    ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int)
)

print(function(17))

Re: Python mit ASM funktioniert nicht.

Verfasst: Samstag 21. Januar 2023, 13:34
von __blackjack__
Das wäre dann wohl die 64 Bit ABI und da wird das erste int-Argument offenbar in ECX übergeben. Dann müsste der Code im ersten Beitrag aber richtig sein. Der Godbold-Code ist ja nicht optimiert, da wird erst ECX in den Stapelrahmen mit den lokalen Variablen geschrieben um den Wert danach gleich wieder von dort auszulesen. Optimiert macht der Compiler da was nettes und ”missbraucht” LEA für die Addition:

Code: Alles auswählen

        lea     eax, DWORD PTR [rcx+42]
        ret     0

Re: Python mit ASM funktioniert nicht.

Verfasst: Samstag 21. Januar 2023, 19:25
von funkheld
---------------------------------
mov eax,23
add eax,12
ret
--------------------------------

asm_function = (
b'\xB8\x17\x00\x00\x00\x83\xC0\x0C\xC3'
)

Das funktioniert oben.

Warum kann man nicht die Variable in eax reinbringen mit : mov eax, ecx ?
Kann man irgendwie eine Adresse dort reinbringen von Python wo die Variable reingelegt wird?

Ich versteh das nicht , was da passiert.

Danke

Re: Python mit ASM funktioniert nicht.

Verfasst: Samstag 21. Januar 2023, 19:27
von __deets__
Wenn du keine Parameter von Python übergeben willst, kannst du auch gleich nasm benutzen…

Re: Python mit ASM funktioniert nicht.

Verfasst: Montag 23. Januar 2023, 08:42
von Sirius3
@funkheld: Dein ursprünglicher Code funktioniert einwandfrei. Um nochmal sicher zu gehen: Du benutzt Python3 in 64bit oder in 32bit?

Re: Python mit ASM funktioniert nicht.

Verfasst: Montag 23. Januar 2023, 09:18
von Sirius3
Noch Anmerkungen zu Deinem Code: ctypes kennt bereits memmove, das selbst zu definieren ist also nicht nötig.
Ich persönlich bevorzuge ein char-Array, weil das klarer und sicherer ist:

Code: Alles auswählen

import ctypes

# https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc#MEM_COMMIT
MEM_COMMIT = 0x00001000
# https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc#MEM_RESERVE
MEM_RESERVE = 0x00002000
# https://learn.microsoft.com/en-us/windows/win32/memory/memory-protection-constants#PAGE_EXECUTE_READWRITE
PAGE_EXECUTE_READWRITE = 0x40
# https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc
virtual_alloc = ctypes.windll.kernel32.VirtualAlloc
virtual_alloc.argtypes = (ctypes.c_void_p, ctypes.c_size_t, ctypes.c_long, ctypes.c_long)
virtual_alloc.restype = ctypes.c_void_p  # LPVOID


asm_function = (
    b'\x8b\xc1'      # mov eax, ecx
    b'\x83\xc0\x05'  # add eax, 5
    b'\xc3'          # ret
)

memory_buffer = virtual_alloc(
    0,                         # lpAddress - NULL
    len(asm_function),         # dwSize
    MEM_COMMIT | MEM_RESERVE,  # flAllocationType
    PAGE_EXECUTE_READWRITE     # flProtect
)

if not memory_buffer:  # VirtualAlloc returned NULL
    raise RuntimeError("VirtualAlloc call failed. Error code:", ctypes.GetLastError())

code_ptr = (ctypes.c_char * len(asm_function)).from_address(memory_buffer)
code_ptr.raw = asm_function

function = ctypes.cast(
    code_ptr,
    ctypes.CFUNCTYPE(
        ctypes.c_int,  # return type
        ctypes.c_int   # argument type
    )
)

print(function(42))

Re: Python mit ASM funktioniert nicht.

Verfasst: Montag 23. Januar 2023, 11:54
von funkheld
Oh , danke für die Info , ich habe 32bit Python 3.9.0
Wie kann man das Programm bitte anpassen, das 32bit-Variablen erkannt werden?
Oder muss ich das Python mit 64bit installieren?

Re: Python mit ASM funktioniert nicht.

Verfasst: Montag 23. Januar 2023, 12:21
von __blackjack__
Naja wenn die 64-Bit ABI funktioniert, wird das ein 64-Bit-Python gewesen sein. Und das erklärt dann natürlich auch warum das bei Dir nicht funktioniert. Bei der 32 ABI kommt das Argument tatsächlich über den Stack mit einem Versatz von 4 Bytes zum Stapelzeiger. Die Routine müsste also aus diesen Bytes bestehen: b"\x8b\x44\x24\x04\x83\xc0\x2a\xc3"

Re: Python mit ASM funktioniert nicht.

Verfasst: Montag 23. Januar 2023, 13:20
von funkheld
Hallo, danke an alle.

Jetzt funktioniert es mit Python 3.9.0 32bit
------------------------------------
b"\x8b\x44\x24\x04\x83\xc0\x2a\xc3"
-----------------------------------
Wie sieht das jetzt oben bitte mit Stack mit einem Versatz von 4 Bytes zum Stapelzeiger aus?

Werde dann mal meine experiemente machen.

Re: Python mit ASM funktioniert nicht.

Verfasst: Montag 23. Januar 2023, 13:50
von __blackjack__
@funkheld: Die Argumente liegen auf dem Stack. Und als erstes liegen dort 4 Bytes Rückkehradresse vom CALL, darum der Versatz. Warum willst Du doch gleich noch mal Assembler verwenden wenn Du nicht weisst, wie man in Assembler Aufrufe mit Argumenten macht‽

Code: Alles auswählen

    mov eax, [esp+4]
    add eax, 42
    ret

Re: Python mit ASM funktioniert nicht.

Verfasst: Montag 23. Januar 2023, 19:58
von funkheld
Ich habe mir jetzt das Buch von Günter Born ( ASM X86) als PDF geladen.
Muss mich jetzt mal tiefer reinknien mit 73 jahren.

Mit welchen einfachen Assembler kann man compilieren und der auch zusätzlich bitte diesen Code erzeugt : \x8B\x44\x24\x04\x8B\x5C\x24\x08\x01\xD8\xC3

Danke.

Re: Python mit ASM funktioniert nicht.

Verfasst: Montag 23. Januar 2023, 20:50
von __blackjack__
@funkheld: Welches Buch denn? Das was ich von dem Autor als PDF gefunden habe nennt/behandelt drei Assembler (A86, MASM, TASM) ist aus den 90ern und behandelt soweit ich das sehe 16-Bit DOS Programmierung. Und warum überhaupt Assembler? Welches tatsächliche Problem denkst Du damit lösen zu können das man nicht in Python oder einer anderen Programmiersprache lösen kann? Eine Funktion die 5 oder 42 auf eine andere Zahl addiert ist es ja eher nicht, das geht mit Python einfacher als wenn man da versucht etwas in Maschinensprache zu schreiben, und das funktioniert dann auch auf allen Plattformen auf denen Python läuft, und nicht nur unter einem 32-Bit-x86 Python für Windows.

Re: Python mit ASM funktioniert nicht.

Verfasst: Dienstag 24. Januar 2023, 12:24
von funkheld
Ich mache das aus Spass zur Sache.
Wüsste auch kein Problem um etwas dringend zu suchen als Sprache was damit erledigt werden muss mit dem Computer.