Also, ich würde gern die IAT von kernel32.dll zur Laufzeit patchen um eine bestimmte Funktion aus ntdll.dll zu ersetzen (wenn IAT nicht klappt, erledige ich das inline über nen Hook). IAT scheint mir mit dem geringsten Aufwand verbunden. Leider bin ich auf ein paar Stolpersteine gestoßen.
Auf einem weitgehend gepatchten Windows 7 x64 mit einem 32-bittigen Prozeß bekomme ich bspw. kernel32.dll an Adresse 773F0000 geladen. IMAGE_IMPORT_DESCRIPTOR für ntdll.dll findet sich an 774BABD4. Dabei benutze ich RtlImageDirectoryEntryToData (ntdll) um den Eintrag für IMAGE_DIRECTORY_ENTRY_IMPORT zu ermitteln.
Und nun wird's seltsam.
Gehe ich nun durch die Liste unter RVAs unter OriginalFirstThunk (im IMAGE_IMPORT_DESCRIPTOR), bekomme ich Werte die als RVA keinen Sinn machen (deutlich größer als eine geladene kernel32.dll), die aber als VA zu Speicherzugriffsfehlern führen.
Die Konvertierung von RVA zu VA überlasse ich dabei übrigens der Funktion RtlImageRvaToVa (ntdll).
Gucke ich mir bspw. die Adressen der Thunks an, sind die total sinnvoll:
- IMAGE_THUNK_DATA[774BBF34] == RVA[000CBF34]
- IMAGE_THUNK_DATA[774BBF44] == RVA[000CBF44]
- IMAGE_THUNK_DATA[774BBF58] == RVA[000CBF58]
- IMAGE_THUNK_DATA[774BBF78] == RVA[000CBF78]
- IMAGE_THUNK_DATA[774BBF8C] == RVA[000CBF8C]
- IMAGE_THUNK_DATA[774BBFA4] == RVA[000CBFA4]
- IMAGE_THUNK_DATA[774BBFB4] == RVA[000CBFB4]
Schaue ich mir die enthaltenen - angeblichen RVAs - an, sehen die wie VAs aus, sind aber außerhalb der geladenen kernel32.dll angesiedelt; und führen, wie schon geschrieben, ins Nirvana:
- IMAGE_IMPORT_BY_NAME[744E00E5]
- IMAGE_IMPORT_BY_NAME[744E0115]
- IMAGE_IMPORT_BY_NAME[745202B1]
- IMAGE_IMPORT_BY_NAME[744E0153]
- IMAGE_IMPORT_BY_NAME[74520483]
- IMAGE_IMPORT_BY_NAME[744E01D3]
- IMAGE_IMPORT_BY_NAME[745204FE]
Hat hier irgendjemand ne Ahnung woher diese unsinnigen Werte rühren und ob es einen Weg gibt diese Zeigerwerte in korrekte Werte zu überführen?
DecodePointer und
DecodeSystemPointer hab ich beide schon ausprobiert. Die Rückgabewerte sehen noch sinnloser aus und führen letztlich auch ins Nirvana.
Nochmal kurz zur Methode:
- Ermittle PIMAGE_NT_HEADERS mithilfe von RtlImageNtHeader (ntdll) aus der Adresse der geladenen kernel32.dll
- Ermittle den ersten IMAGE_IMPORT_DESCRIPTOR der geladenen kernel32.dll mithilfe von RtlImageDirectoryEntryToData(..., TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, ...)
- Gehe durch die IMAGE_IMPORT_DESCRIPTOR-Records bis der .Name irgendwann 0 als RVA-Wert hat
- Benutze .Name um den IMAGE_IMPORT_DESCRIPTOR für ntdll.dll aufzuspüren
- Konvertiere RVA für .OriginalFirstThunk mit RtlImageRvaToVa zu einer VA (Zeiger auf eine Liste mit RVAs)
- Iteriere über die Liste der RVAs welche zu IMAGE_THUNK_DATA zeigen
- Benutze .AddressOfData aus dem IMAGE_THUNK_DATA-Eintrag um die (R)VA zum Namen der Funktion zu ermitteln