pydbg Callbackfunktion Register auslesen

#1
Hallo Community!

ich beschäftige mich gerade etwas mit python und dem pydbg. Dazu lese ich gerade das Buch hacking with Python welches sehr interessant ist aber leider mit einigen Fehlern :( naja gerade bin ich wohl wieder auf solch einen Fehler gestoßen und komme alleine nicht weiter...

Ich habe ein Programm das nennt sich printf_loop.py es tut nichts anderes als die printf funktion aus der dll MSVCRT.dll in einer while aufzurufen und die Durchläufe in einem Counter hozuzählen und auf der Console auszugeben sieht folgerndermaßen aus:

Code:
import ctypes

import time

msvcrt = ctypes.CDLL("MSVCRT")

counter = 0

while 1:
    msvcrt.printf("Loop iteration %d!\n" % counter)
    time.sleep(2)
    counter += 1
nun habe ich eine Callbackfunktion für den pydbg geschrieben ich möchte damit einen Breakpunkt setzen den Wert des Counters von printf_loop.py auslesen und eine Random zahl in diesen Counter schreiben. Dabei soll meine Callbackfunktion den richtigen counterwert bei jedem durchlauf ausgeben mein printf_loop soll jedoch die random zahl ausgeben
(ich hoffe ich habe es soweit verständlich erklärt ?)
Im buch sieht das so aus:

Code:
from pydbg import *
from pydbg.defines import *

import struct 
import random

# Dies ist unsere benutzerdefinierte Callback-Funktion

def printf_randomizer(dbg):
    
    # Lese den Wert des Zaehlers an ESP + 0x8 als DWORD ein 
    parameter_addr = dbg.context.Esp + 0x8
    counter = dbg.read_process_memory(parameter_addr,4)
    
    # read_process_memory liefert uns einen gepackten Binaerstring
    # zurueck. Wir muessen ihn erst entpacken, bevor wir ihn weiter nutzen koennen.
    
    counter = struct.unpack("L",counter)[0]
    print "Counter: %d" % int(counter)
    
    # Generiere eine Zufallszahl und packe sie in ein Binaerformat,
    # damit sie korrekt in den Prozess zurueck geschrieben werden kann,
    random_counter = random.randint(1,100)
    random_counter = struct.pack("L", random_counter)[0]
   
    # Nun fuegen wir die Zufallszahl ein und lassen den Prozess #weiterlaufen.
   dbg.write_process_memory(parameter_addr, random_counter)
    
    return DBG_CONTINUE

# die pydbg-Klasse instanzieren
dbg = pydbg()

# Die PID des printf_loop.py-Porzesses eingeben
pid = raw_input("Enter the printf_loop.py PID: ")

#Debugger an diesen Prozess ankoppeln
dbg.attach(int(pid))

#Breakpunkt festlegen und die von uns definierte printf_randomizer_Funktion
#als Callback eingeben
printf_address = dbg.func_resolve("msvcrt","printf")
dbg.bp_set(printf_address,description="printf_address",handler=printf_randomizer)

#Prozess fortsetzen

dbg.run()
Leider funktioniert das ganze so nicht!
Also die Callbackfuntkion gibt riesigen werte aus meiner Meinung nach die Adresse des ESP ?
und printf_loop läuft einfach ganz normal weiter und gibt den Counter aus den man erwartet ...

Meiner Meinung nach besteht der Fehler doch darin, das das ESP register ausgelesen wird....
muss nicht aber das ECX register gelesen werden da dies doch das Zählregister ist ? Wen ich den code ändere also ECX auslese bricht mir das ganze ab bei read_process_memory aufgrund der Länge die übergeben wird.
Nun habe ich versucht die Länge mit sizeof zu bestimmen allerding wird mir gesagt das der typ parameter_addr keine size hat ...

Nun ist meine Frage ist meine Annahme überhaupt richtig oder bin ich da auf dem Holzweg ? wenn ja wie komm ich denn an dem Zählwert von ECX ran und wie bekomm ich die richtige länge raus ?

Danke für euere Hilfe
 

CDW

Moderator
Mitarbeiter
#2
muss nicht aber das ECX register gelesen werden da dies doch das Zählregister ist ?
ECX ist ein "Zählregister" nur für einige Anweisungen (REP, LOOP) sowie bei "klassischen" Asm-Einführungen/Büchern, ansonsten hält sich schon lange kein Compiler an diese "Konvention" ;).

Zudem muss (und wird) CPython den Counter nicht nur in ECX speichern. Das wird eher irgendwo ein Objekt/speichergebundene Variable sein.
Das Interna ist hier auch eher unwichtig - interessant ist die Paramterübergabe bei cdecl - Konvention:
http://de.wikipedia.org/wiki/Aufrufkonvention
http://en.wikibooks.org/wiki/X86_Disassembly/Calling_Conventions
http://en.wikibooks.org/wiki/X86_Disassembly/Calling_Convention_Examples
Die Parameter werden also über den Stack übergeben.

Der Code liest also ESP aus, um an den Stack zu kommen. ESP + 0x8 = Speicheradresse der zweiten übergebenen Variable.
Liest man stattdessen ECX aus, hat man irgendeinen Wert - versucht man darauf read_mem anzuwenden, liest man ggf. ungültigen Speicher (daher auch der Fehler - wenn auch mit der verwirrenden Meldung bezüglich der Länge).

Code:
 msvcrt.printf("Loop iteration %d!\n" % counter)
% ist pythonformatter. Daher wird msvcrt.printf nur einen "fertigen" String "Loop iteration wertx!\n" zu sehen bekommen und damit nur einen Paramter (Stringadresse). Speicher an ESP + 0x8 hat dann also nichts mit dem counter zu tun.

Einfach mal
Code:
msvcrt.printf("Loop iteration %d!\n", counter)
probieren.
 
#3
Danke für deine Hilfe funktioniert wunderbar...

Irgenwie hatte ich einen Denkfehler drinn ich wollte praktisch

Code:
counter += 1
diesen counter verändern und nicht den Parameter deshalb bin ich auch
auf das ECX register gekommen.
 
Oben