Hier will ich mal was weiterführendes zum Thema Trainer ansprechen: Versionsunabhängigkeit oder zumindest Versionstolleranz. ^^
Es gibt nicht viele Trainer, die dieses Feature haben. Ich kenn eigentlich sogar garkeinen, aber möglich ist das.
Ok, die Sache ist nicht ganz einfach. Das geht weit über normale simple Trainer, die Instruktionen statisch auf fixen Adressen patchen hinaus. Und ich hab das vor kurzem erst selbst zum ersten mal gemacht. Drumm ist das hier kein Tutorial und ich würde mich über eine Diskussion zu dem Thema freuen, falls hier genügend Leute mit Intresse an Trainern sind. Es gibt sicherlich noch einiges zu verbessern an meiner Methode.
Um das ganze zu verstehen sind folgende Kenntnisse von Nöten:
- Grundkenntnisse in 32 Bit x86 Assembler
- Kenntnisse über das Speichermodell unter Win32/64
- Kenntnisse über die generelle Funktionsweise von Game-Trainern
- Wissen was man unter DLL-Injection versteht
Damit ihr euch eine Vorstellung machen könnt, beschreibe ich euch erstmal wofür ich das in meinem Projekt brauche und wie ich das im groben umgesetzt habe. Details zu dem Projekt darf ich leider keine nennen. Vieleicht zu einem späteren Zeitpunkt. Was ich sagen darf ist, das es sich um ein Hackingtool handelt, welches dazu designt ist dabei zu unterstützen ein bestimmtes Spiel auf Cheat-Schwachstellen während der Betaphase zu testen.
Ok, los gehts: Mein Tool hookt die ingame-Konsole des Spiels um dort seine eigenen Kommandos zu implementieren. Das ganze läuft natürlich über einen Inline-Hook. Das heisst mit jedem neuen Build des Spiels ändert sich die Adresse und in diesem speziellen Fall auch Pointeradressen, welche mein Tool benötigt um Text in der Konsole auszugeben. Das ist ziemlich nervig da aktuell wöchentlich eine neue Beta-Version des Spiels gebaut wird.
So kam die Idee das ganze etwas dynamischer zu machen.
In diesem Fall können wir davon ausgehen das sich der Zielcode selbst nicht ändert, da es sehr unwarscheinlich ist, das große Änderungen am Konsolencode nötig sind. Allerdings sorgen die massiven Codeänderungen bei jedem Build dafür das sich Codeadressen und Pointeradressen verschieben. Lösung ist es nun sich den Code anzuschauen und nun nach seinem binären Muster zu suchen. Ist dieses gefunden kann man daran die Adresse zum Codepatchen ermitteln und die Pointeradressen aus dem Code auslesen.
Damit das ganze sehr schnell über die Bühne geht sollte man zum einen mit DLL-Injection arbeiten um direkte Operationen auf den virtuellen Speicher ausführen zu können und nicht den Umweg über ReadProcessMemory gehen zu müssen. Zweitens sollte man sich die Speicherregionen mit Ausführengsrechten rauspicken und nur in dehnen suchen. Um es nochmal zu optimieren kann man dann auch nur auf das jeweilige Modul begrenzen. Aber wenn man alleine schon die ganzen Datenregionen ausschließt läuft die binäre Suche sowieso im Sekundenbruchteil ab.
Hier mal ein Beispielcode zum raussuchen der Coderegionen im Prozessspeicher:
PureBasic 4.20 Code
Code: Alles auswählen
Structure Region
BaseAddr.l
Size.l
EndStructure
Procedure.l GetExecutableMemRegions(RegionList.Region(1))
Define.l Addr,RetVal,RegionCnt
Define.MEMORY_BASIC_INFORMATION MemoryInfo
RegionCnt = -1
Repeat
Addr = Addr + MemoryInfo\RegionSize
RetVal = VirtualQuery_(Addr,MemoryInfo,SizeOf(MEMORY_BASIC_INFORMATION))
If RetVal > 0
If MemoryInfo\State = #MEM_COMMIT
If MemoryInfo\Protect = #PAGE_EXECUTE Or MemoryInfo\Protect = #PAGE_EXECUTE_READ Or MemoryInfo\Protect = #PAGE_EXECUTE_READWRITE Or MemoryInfo\Protect = #PAGE_EXECUTE_WRITECOPY
RegionCnt = RegionCnt + 1
ReDim RegionList.Region(RegionCnt)
RegionList(RegionCnt)\BaseAddr = MemoryInfo\BaseAddress
RegionList(RegionCnt)\Size = MemoryInfo\RegionSize
EndIf
EndIf
EndIf
Until RetVal < 1
ProcedureReturn RegionCnt
EndProcedure
Nehmen wir ein simples Beispiel.
Das ist eine Codestelle im Spiel, welche Text auf der Konsole ausgibt. Mein Tool soll nun die Adresse der Ausgabeprozedur herausfinden, welche dort aufgerufen wird und ausserdem noch eine Pointeradresse, von der ich keine Ahnung habe wozu die gut ist aber ich muss den Pointer als Parameter für die Textausgabe verwenden. Solange es funktioniert is mir erstmal wurscht wozu der Pointer gut ist.

Code: Alles auswählen
00401960 68 8C205E00 PUSH 005E208C
00401965 FF35 24996600 PUSH DWORD PTR DS:[669924]
0040196B E8 434C1100 CALL 005165B3
00401970 59 POP ECX
00401971 59 POP ECX
PUSH DWORD PTR DS:[669924] Übergabe eines unbekannten Pointers
CALL 005165B3 aufruf der Prozedur zur Textausgabe
POP ECX Stack aufräumen
POP ECX Stack aufräumen
Daraus ergibt sich folgendes Binärmuster. Die Fragezeichen symbolisieren Bytes, die sich ändern können.
Code: Alles auswählen
68 ?? ?? ?? ?? FF 35 ?? ?? ?? ?? E8 ?? ?? ?? ?? 59 59
PureBasic v4.20 Code
Code: Alles auswählen
Define.l i,RegionCnt,RegionStart,RegionSize,FoundAddr
Dim RegionList.Region(0)
RegionCnt = GetExecutableMemRegions(RegionList())
For i = 0 To RegionCnt
RegionStart = RegionList(i)\BaseAddr
RegionSize = RegionList(i)\Size - 20
FoundAddr = 0
!mov edi,[p.v_RegionStart]
!mov ecx,[p.v_RegionSize]
!mov esi,edi
!add esi,ecx
!GCP_LoopStart:
!mov al,$68
!repne scasb
!mov ax,[edi+4]
!cmp ax,$35FF
!jne GCP_NotFound
!mov al,[edi+10]
!cmp al,$E8
!jne GCP_NotFound
!mov ax,[edi+15]
!cmp ax,$5959
!jne GCP_NotFound
!mov [p.v_FoundAddr],edi
!jmp GCP_Found
!GCP_NotFound:
!cmp edi,esi
!jl GCP_LoopStart
!GCP_Found:
If FoundAddr <> 0
ConsolePointer = PeekL(FoundAddr+6)
ConsoleOutputProcAddr = PeekL(FoundAddr+11) + FoundAddr + 15
Break
EndIf
Next
Was haltet ihr davon? Wo seht ihr Probleme, Verbesserungsmöglichkeiten, Schwachstellen?
Vorallem für ne Prozedur mit der man binär den Speicher durchsuchen kann und die Wildcards unterstützt, wäre ich dankbar.