winmine - codesection per API im memory patchen! - aber wie !?

hi,
das wird mein erster beitrag! ich hab ein kleines coding problem und hoffe auf eure mithilfe. also seit 2 wochen versuchte ich mich mit purebasic ... ist auch nicht soo schwer. aber wenn man richtige was machen möchte, kommt man um die ein oder andere winapi nicht herum. gottseidank hab ich zumindest einige reversing-vorkenntnisse, dennoch raffe ich nicht wirklich viel!

Es geht darum, dass ich winmine.exe = also das windows minesweeper in der codesection mit hilfe der API modifizieren möchte! als beispiel wählte ich mir den offset: $0101E23A was dem "B" = hex 42 von Benutzerdefiniertes Spiel im Titel(!) entspricht. also wenn Ihr auf Spiel / Benutzerdefiniert klickt und dann den title betrachtet wisst Ihr was gemeint ist!

also , wie gesagt, ich wollte das teil als beispiel modifizieren und versuchte mal ein kleines "b" also hex "62" dort hinzuschreiben... ging natürlich nicht. und dann fing die suche an. ich bekam irgendwann raus, dass folgende zeile nicht den vollzugriff garantiert:
ProcessHandle = OpenProcess_(#PROCESS_ALL_ACCESS, #True, ProgID)
dann irgendwann später kam ich, als nix-api-versteher drauf,dass man per VirtualProtectEx noch den zugriff setzten sollte.... das bekomme ich aber gar nicht hin. ich hab folgende zeile zugefügt:
VirtualProtectEx_(ProcessHandle,StartLeseOffset1,4,#PAGE_EXECUTE_READWRITE ,2)

das kann auch totaler blödsinn sein oder fehlt noch davor etwas um diese funktion "einzuleiten" ... ich hoffe ihr könnt helfen!

ok, ich poste mal den ganzen code:

Code:
;Variablen für die Zielapplikation
Prozessname.s = "winmine2.exe"
StartLeseOffset1 = $0101E23A
NeuerHexwert1 = $62000000
;Zeit = $11

;Läuft alle Prozesse durch und sucht nach "Prozessname"
ExamineProcesses()
While NextProcess()
  If GetProcessName()=Prozessname
    ProgID = GetProcessPID()
  EndIf
Wend

;Öffnen der Commandline für Ausgabe
OpenConsole()

;Zeigt ProzessID
PrintN("Prozess ID:"+Str(ProgID))
Delay(5000)

;Lesen der Speicherstelle per API
ProcessHandle = OpenProcess_(#PROCESS_ALL_ACCESS, #True, ProgID)
ReadProcessMemory_(Processhandle, StartLeseOffset1, @Zeit, 4, 0)
VirtualProtectEx_(ProcessHandle,StartLeseOffset1,4,#PAGE_EXECUTE_READWRITE ,2)

PrintN("Zeit bzw alter Hexwert VOR schreiben:"+Str(Zeit))



;Simple Schleife zum Debuggen/Fehlersuche
x = 10
Repeat 
PrintN("Neuer Wert wird gleich geschrieben:"+Hex(NeuerHexwert1))

;Schreiben der Speicherstelle
VirtualProtectEx_(ProcessHandle,StartLeseOffset1,4,#PAGE_EXECUTE_READWRITE ,1)
WriteProcessMemory_(ProcessHandle, StartLeseOffset1, NeuerHexwert1, 4, 1)
PrintN("wurde geschrieben...")
Delay(2000)
ReadProcessMemory_(Processhandle, StartLeseOffset1, @Zeit, 1, 0)
PrintN("Lese ob Zeit bzw. alter Hexwert durch neuen ersetzt ist ?:"+Str(Zeit))
Delay(5000)
x-1
Until x = 0
 
nicht jeder von uns hat purebasic zum testen ;)
Gibt es Kompilierprobleme oder eher Probleme zur Laufzeit? Z.B möchte ja die WriteProcessMemory Funktion als letzen Parameter einen
pNumberOfBytesWritten

A pointer to a variable that receives the number of bytes transferred into the specified process. This parameter is optional. If lpNumberOfBytesWritten is NULL, the parameter is ignored.
haben. Du hast da aber eine 1 stehen.
Beschreib das Problem doch bitte ein wenig - ich vermute, man kann hier den Fehler auch ohne genauere purebasic Kenntnisse finden.

Was mir so auffällt
ProcessHandle = OpenProcess_(#PROCESS_ALL_ACCESS, #True, ProgID)
Du hast schon richtig vemutet, dass man erstmal keine Garantie hat, den Zugriff zu bekommen. Normalerweise holt man sich erstmal die nötigen Privilegien zum "Prozessöffnen".
Das nennt sich "SeDebugPrivilege"
C++ OpenThread funktioniert nicht
ich glaube, hier ist PB code (obs richtig ist, kann ich leider nicht beurteilen)
http://www.purebasic.fr/german/viewtopic.php?t=16180&sid=
im Zweifelsfalle suche einfach nach SeDebugPriviledge oder TOKEN_ADJUST_PRIVILEGES + Sprache.
Das Gemeine hier ist, dass man als Programmierer öfters mal den Debugger anwirft und (nach meiner Beobachtung unter 2k/XP) dann braucht ein Programm diese Privilegien erstmal nicht zu holen - sobald ein Debugger gelaufen ist, scheinen sie automatisch dazuzukommen.Dann kommt es zu langer Fehlersuche, warum der Patcher auf dem eigenen Rechner funktioniert, auf anderen Rechnern aber nicht.

Weiterhin: bei der Fehlersuche mit dem Debugger wird der Prozess normalerweise vom Debugger gestartet und erbt damit all seine Rechte/Privilegien. Ein Debugger darf aber standardmäßig recht viel (oder holt sich die Rechte, sofern es möglich ist).
D.h dass man solche "Rechtekonflikte" im Debugger nicht unbedingt sieht, da die Anwendung dann mit den "erweiterten" Rechten läuft und der Patch problemlos klappt.

Einfacher ist es, wenn man ein Prozess selber startet (mit CreateProcess) - notfalls macht man danach einen Sleep(1000) oder WaitForSingleObject Function bis es läuft.
Da das eigene Programm damit der "Vater" ist, kann man das "Kind" manipulieren, wie man möchte (man hat direkt Lese/Schreibrechte).


Ansonsten: jede API hat normalerweise einen Rückgabenwert
Return Value

If the function succeeds, the return value is nonzero.
damit kannst Du schon mal direkt im Programm testen, ob die Funktion erfolgreich war.
 
danke für die schnelle und aufwendige antwort !

zur problembeschreibung:

das programm läuft fehlerfrei durch und am ende gerät es ja in die repeatschleife und versucht immer in den prozess zu schreiben, dann ließt es wieder .... das ganze wird in der konsole ausgegeben.

der prozess winmine.exe muss schon laufen, sonst gehts natürlich nciht. ich habs mal kompiliert und angehängt.

eine weitere variante die ich mit hilfe eines PB-board-mitglieds "entwickelt" habe poste ich auch. (source und exe) dabei bin ich mit nicht sicher ob ich durch diese integration schon deine beschriebene variante des father - child prozesses erreiche !???

Code:
;Beispiel für WinMine (Windows XP)
;Speicher zum Auslesen mehrerer Bytes reservieren, hier 50 Bytes
Buffer = AllocateMemory(50)  ;Buffer ist schon die Speicher-Adresse!
SystemDir$ = Space(255)
FileL = GetSystemDirectory_(SystemDir$, 255)
WinMineDir$ = Left(SystemDir$, FileL) + "\winmine.exe"
hProgram = RunProgram(WinMineDir$, "", GetCurrentDirectory(), #PB_Program_Open)
;damit die Programm-ID ermitteln; ist die, die auch der Task-Manager anzeigt (variabel!!!)
ProgID = ProgramID(hProgram)
;jetzt das Ganze für Vollzugriff öffnen
ProcessHandle = OpenProcess_(#PROCESS_ALL_ACCESS, #True, ProgID)

;Adresse festlegen
StartLeseOffset1 = $0101E23a ;also Strings von WinMine. Dürften im Quellcode in einer Nur-Read-Section liegen
;in den reservierten Speicher lesen wir ab der Start-Adresse 50 Bytes ein
ReadProcessMemory_(Processhandle, StartLeseOffset1, Buffer, 50, 0)
;Anzeige des Original-Textes, Unicode!!

;Vollzugriff ist das so richtig ????
VirtualProtectEx_(ProcessHandle,StartLeseOffset1,Buffer,#PAGE_EXECUTE_READWRITE ,#Null)

;Öffnen der Konsole zu Fehlerkontrolle
OpenConsole()
PrintN("Der Buffer mit dem ausgelesenem String VOR schreiben: "+PeekS(Buffer, 50, #PB_Unicode))
Delay(5000)


;jetzt manipulieren wir den Text im Speicher
PokeC(Buffer, 98)  ;98 oder $62 = "b". Check auf Unicode sollte vorher erfolgen. Wenn
PrintN("im Buffer aendern wir den String, also B zu b : "+PeekS(Buffer, 50, #PB_Unicode))
Delay(5000)
    ;in einem Deutsch-Sprachigem Programm bei Texten jedes 2.Zeichen den (echten) Wert Null
    ;hat, kann davon ausgegangen werden, das Unicode vorliegt.   
;und schreiben ihn zurück
If WriteProcessMemory_(ProcessHandle, StartLeseOffset1, Buffer, 50, #Null)
PrintN("Jetzt wurde der String (versucht!!) zu schreiben mit WriteProcessMemory!")
Delay(5000)
  ;und lesen ihn wieder aus
  ReadProcessMemory_(ProcessHandle, StartLeseOffset1, Buffer, 50, 0)
PrintN("(Erfolg!) Jetzt wurde er per ReadProcessMemory gelesen und hier das Ergebnis: "+PeekS(Buffer, 50, #PB_Unicode))
Delay(5000)

 ;oder das Rückschreiben schlug fehl
 Else
PrintN("Nicht erfolgreiche Rueckgabe! ")
Delay(5000)
 ;jetzt (viel zu spät, testet man natürlich vorher) checken wir den Zugriffs-Status
 ; auf die gewählte Speicheradresse. Das "PROCESS_ALL_ACCESS" bei OpenProcess garantiert
 ; keinen automatischen Schreibzugriff! Priorität hat die Section-Read-Write-Zuordnung
 ; des Source-Codes!     
 ;Structure MBI definieren; siehe auch Strukturverzeichnis von PB!
 ;PAGE_READONLY = 2     sind im Protect-Status die interessierenden Werte
 ;PAGE_READWRITE = 4    teste mal mit Speicheradresse von Zeit!
  MBI.MEMORY_BASIC_INFORMATION
 ;Status-Informationen einholen
  VirtualQueryEx_(ProcessHandle, StartLeseOffset1, MBI, SizeOf(MEMORY_BASIC_INFORMATION))
  Status = MBI\Protect
PrintN("Speicher-Zugriffs-Status wurde per VirtualQueryEx analysiert Ergebnis = " + Str(Status))
Delay(5000)
  If Status = 2    ;also nur READONLY
PrintN("Zugriffsrecht verweigert! - erneut versuchen bitte!") 
Delay(10000)  
   Else
PrintN("Zugriff erlaubt!")
Delay(10000)
  EndIf 
EndIf

ich habe beide varianten als exe angehängt! evtl hilft das!?

EDIT: Ich hab die lösung und versteh sie nicht. ich zitiere mal den lösungsgeber:
Füge vor dem Schreiben
Code:

VirtualProtectEx_(ProcessHandle, StartLeseOffset1, 50, #PAGE_EXECUTE_READWRITE , @A)

ein; A ist eine normale Long-Variable.

?( ?( ?( ?( ?( X( X( X( X( X( X( X( Also das @ist in PB immer ein Pointer auf eine Variable oder Speicherstelle (So weit ich weiss) - aber wo zur Verdammten Hölle kommt das "A" her ? - es geht wirklich damit!!! - Verdammt ich müchte es gerne verstehen!
 
Also, bei Variante1: wie erwähnt, erstmal Debugpriviledge holen.
Dann bei
Code:
VirtualProtectEx_(ProcessHandle,StartLeseOffset1,4,#PAGE_EXECUTE_READWRITE ,2)
wird als letzer Paramter ein Pointer zu einer Variable erwartet, wo die API den alten Wert speichert. Du darfst weder 2 noch 0 schreiben. Es muss schon die Adresse einer Variable sein:
http://msdn.microsoft.com/en-us/library/aa366899(VS.85).aspx
lpflOldProtect

A pointer to a variable that receives the previous access protection of the first page in the specified region of pages. If this parameter is NULL or does not point to a valid variable, the function fails.
dasselbe gilt für
Code:
VirtualProtectEx_(ProcessHandle,StartLeseOffset1,4,#PAGE_EXECUTE_READWRITE ,2)
Wenn ich das im Debugger korrigiere, kommt der nächste Part:
weiterhing
Code:
riteProcessMemory_(ProcessHandle, StartLeseOffset1, NeuerHexwert1, 4, 1)
wieder: letzer Parameter sollte eine Adresse sein oder 0
sonst wird die Zahl 1 als Adresse genommen und es gibt einen schönen Absturz.

Zusätzlich ist NeuerHexwert1 bei Dir KEINE Adresse eines Buffers, sondern direkt der Wert - da ist aber eine Adresse gefragt:
lpBuffer

A pointer to the buffer that contains data to be written in the address space of the specified process.
ich vermute Stark, das wird mit @ erreicht, da Du es bei ReadProzess verwendest (und dieser korrekt funktioniert).
http://msdn.microsoft.com/en-us/library/ms681674(VS.85).aspx

bei Variante2 ist :
VirtualProtectEx_(ProcessHandle,StartLeseOffset1,Buffer,#PAGE_EXECUTE_READWRITE ,#Null)
Buffer= hier sollte 'size' hin . Im fertigen Programm steht ein Wert ~ 41 000 000 was doch etwas ungünstig ist

dann wieder:
Code:
WriteProcessMemory_(ProcessHandle, StartLeseOffset1, Buffer, 50, #Null)
hier darf keine NULL hin!

Sobald ich die beiden Fehler korrigiere, klappt das Patchen an sich. Nur wird man keine Änderungen sehen (falls das Menü gemeint ist, was anderes finde ich an der Adresse nicht vor), weil Du den Titel in den "Ressourcen" patchst. Nach dem das Programm aber sein Fenster mit dem Titel/Menüs erstellt hat, liegen all diese Dinge im Speicher der grafischen Bibliotheken - sie werden i.r nur einmal zum erstellen benutzt und nicht jedesmal für die Anzeige ausgelesen.
 
danke dir für deine einsatzbereitschaft zu so später stunde! hat jetzt alles wunderbar geklappt .... ich hab zum schluss noch die dinge korrigiert die du mir geschrieben hast und gleich zum testen einen laufenden memorypatch ... also trainer für hurrican geschrieben. funktioniert!

jetzt muss ich mich nurnoch dransetzen und das ganze irgendwie so übersichtlich gestallten, dass ich auch bei mehreren zu patchenden stellen alles zum laufen bekommen.

als größtes problem sehe ich, dass ich nur max dwords in hex in dem buffer schieben kann. d.h. alles was über 8 bytes geht muss ich anderes "behandeln". wie weiss ich noch nicht.

ein menü drumherum zu bauen wird wohl das kleinste sein...

achso, wie ist das eigentlich mit vista ? - da wird´s wohl vorbei sein mit memorypatches bzw loadern .... etc ? oder gibts da lösungen ?
 
Zurück
Oben