Hier mal ne grobe zusammenfassung
Ein prozess hat seinen eigenen vrituellen Speicherbereich bestehend aus TEXT (dem programmcode) DATA (konstanten/globalen variablen) HEAP und STACK.
Cheatengine basically ließt das TEXT segment aus und disassembled das. Dafür benötigst du aber keine Runtime analyse, du kannst genauso gut die Executable disassembeln und am ende die basisaddresse an die die libc geladen wurde drauf rechnen.
Kleines beispiel unter Linux, mit dem gdb. Ich will die addresse von sprintf aus der LibC in meinem gdb finden (ignorieren wir mal das ich einfach print &sprintf eingeben könnte).
Zunächst disassemble ich die libc mit objdump.
Code:
$ objdump -T /usr/lib/libc.so.6
/usr/lib/libc.so.6: file format elf64-x86-64
DYNAMIC SYMBOL TABLE:
[...]
0000000000059530 g DF .text 00000000000000b6 GLIBC_2.2.5 sprintf
[...]
Die ausgabe sagt mir jetzt die Funktion sprintf ist in addresse 0x059530. Die ist, solange sich die libc version nicht ändert, immer gleich.
Jetzt gilt es die basisadresse des moduls herauszufinden. Dafür starte ich mein programm im gdb, breake und gebe mir das memory mapping aus:
Code:
(gdb) info proc mappings
process 112443
Mapped address spaces:
Start Addr End Addr Size Offset objfile
0x555555554000 0x555555555000 0x1000 0x0 /home/frederic/a.out
0x555555555000 0x555555556000 0x1000 0x1000 /home/frederic/a.out
0x555555556000 0x555555557000 0x1000 0x2000 /home/frederic/a.out
0x555555557000 0x555555558000 0x1000 0x2000 /home/frederic/a.out
0x555555558000 0x555555559000 0x1000 0x3000 /home/frederic/a.out
0x7ffff7dcf000 0x7ffff7df1000 0x22000 0x0 /usr/lib/libc-2.28.so
0x7ffff7df1000 0x7ffff7f3c000 0x14b000 0x22000 /usr/lib/libc-2.28.so
0x7ffff7f3c000 0x7ffff7f88000 0x4c000 0x16d000 /usr/lib/libc-2.28.so
0x7ffff7f88000 0x7ffff7f89000 0x1000 0x1b9000 /usr/lib/libc-2.28.so
0x7ffff7f89000 0x7ffff7f8d000 0x4000 0x1b9000 /usr/lib/libc-2.28.so
0x7ffff7f8d000 0x7ffff7f8f000 0x2000 0x1bd000 /usr/lib/libc-2.28.so
0x7ffff7f8f000 0x7ffff7f95000 0x6000 0x0
0x7ffff7fce000 0x7ffff7fd1000 0x3000 0x0 [vvar]
0x7ffff7fd1000 0x7ffff7fd3000 0x2000 0x0 [vdso]
0x7ffff7fd3000 0x7ffff7fd5000 0x2000 0x0 /usr/lib/ld-2.28.so
0x7ffff7fd5000 0x7ffff7ff4000 0x1f000 0x2000 /usr/lib/ld-2.28.so
0x7ffff7ff4000 0x7ffff7ffc000 0x8000 0x21000 /usr/lib/ld-2.28.so
0x7ffff7ffc000 0x7ffff7ffd000 0x1000 0x28000 /usr/lib/ld-2.28.so
0x7ffff7ffd000 0x7ffff7ffe000 0x1000 0x29000 /usr/lib/ld-2.28.so
0x7ffff7ffe000 0x7ffff7fff000 0x1000 0x0
0x7ffffffde000 0x7ffffffff000 0x21000 0x0 [stack]
Hier kann ich sehen das die libC an addresse 0x7ffff7df1000 geladen wurde. Addiere ich also die addressen auf 0x7ffff7df1000 + 0x059530 = 0x7ffff7e28530 (Ein schnelles print &sprintf verifiziert die rechnung).
Mit diesem Funktionspointer kann ich jetzt was anfangen. Wenn ich jetzt z.B. im GDB p ((int (*) (const char *)) 0x7ffff7e28530) (ToAddr, "ABC") eintippen würde, würde die gdb die Funktion sprintf ausführen, und basically ABC an die addresse ToAddr schreiben.
GDB macht das ganze richtig, Pausiert die prozess bereitet das memory vor (stack frame, register, etc.) und macht dann den call. Beschrieben ist das hier:
[Only registered and activated users can see links. Click Here To Register...]. Während du das so auch versuchen könntest zu basteln, gibt es glaube ich einen weitaus schmerzloseren weg.
Zurück zu Windows. Um funktionen in Assemblies (.exe, .dll) zu finden kannst du z.B. IDA pro oder Binary Ninja verwenden, zwei state of the Art disassembler die noch viel mehr funktionalität haben, aber auch super einfach dir alle funktionen anzeigen können. Du disassemblest damit einfach die binary, suchst die funktion die du willst, und schreibst dir die Addresse auf
Um nun die Basisadresse zu bekommen ohne einen debugger zu verwenden, bietet dir die WinAPI folgende Möglichkeit:
[Only registered and activated users can see links. Click Here To Register...].
Zu guter letzt kannst du den Funktionspointer den du dir einfach aus Basisadresse + Funktionsaddresse ausgerechnet hast mithilfe von
[Only registered and activated users can see links. Click Here To Register...] erzeugen.
Aber achtung, die Funktion wird wie der name schon sagt in einem Separaten Thread ausgeführt, es kann also zu Raceconditions führen. Wenn du das programm während dessen anhalten willst musst du es so machen wie es der GDB macht