Entwickler-Ecke
Windows API - API hook
uall@ogc - Do 26.08.04 13:59
Titel: API hook
hab hier mal nen kleinen API hook geschrieben:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116:
| var nextMessageboxA: function(hWnd: HWND; lpText, lpCaption: pchar; uType: cardinal): UINT; stdcall; oldMessageboxA: function(hWnd: HWND; lpText, lpCaption: pchar; uType: cardinal): UINT; stdcall;
function myMessageboxA(hWnd: HWND; lpText, lpCaption: pchar; uType: cardinal): UINT; stdcall; begin result := nextMessageboxA(hWnd,pchar(lpText+' hooked!!!'),lpCaption,uType); end;
procedure HookApi(my, old: pointer; var next: pointer); stdcall; asm push PAGE_EXECUTE_READWRITE push MEM_COMMIT or MEM_RESERVE push 64+5*4 push 0 call VirtualAlloc push eax mov [eax], $C7241C8B; add eax, 4 mov [eax], $00002404; add eax, 4 mov [eax], $058b0000; add eax, 4 mov [eax], $00000000; add eax, 4 mov [eax], $0000158B; add eax, 4 mov [eax], $10890000; add eax, 4 mov [eax], $0000158B; add eax, 4 mov [eax], $50890000; add eax, 4 mov [eax], $50E0FF04; add eax, 4 mov [eax], $00058B52; add eax, 4 mov [eax], $8B000000; add eax, 4 mov [eax], $00000015; add eax, 4 mov [eax], $8B108900; add eax, 4 mov [eax], $00000015; add eax, 4 mov [eax], $04508900; add eax, 4 mov [eax], $E3FF5A58; add eax, 4 pop eax push eax
mov ebx, eax add eax, 6 add ebx, $23 mov [eax], ebx
add eax, 6 add ebx, $40-$23 mov [eax], ebx
add eax, 6 add ebx, 4 mov [eax], ebx
add eax, 8 add ebx, 4 mov [eax], ebx
add eax, 13 sub ebx, 8 mov [eax], ebx
add eax, 6 add ebx, 12 mov [eax], ebx
add eax, 8 add ebx, 4 mov [eax], ebx
push eax push esp push PAGE_EXECUTE_READWRITE push 8 push [old] call VirtualProtect
pop eax pop eax push eax add eax, $40 mov ebx, [old] mov [eax], ebx add eax, 4 mov ecx, [ebx] mov [eax], ecx add ebx, 4 add eax, 4 mov ecx, [ebx] mov [eax], ecx
mov eax, [old] mov byte ptr [eax], $E9 add eax, 1 mov ebx, [my] sub ebx, [old] sub ebx, 5 mov [eax], ebx
pop eax push eax add eax, $40 add eax, 12 mov ebx, [old] mov ecx, [ebx] mov [eax], ecx add eax, 4 add ebx, 4 mov ecx,[ebx] mov [eax], ecx
mov eax, [next] pop edx mov [eax], edx end;
procedure test; begin @oldMessageBoxA := GetProcAddress(LoadLibrary('user32.dll'),'MessageBoxA'); HookApi(@myMessageboxA,@oldMessageBoxA,@nextMessageboxA); oldMessageboxA(0,'hallo :) ',nil,0); end; |
das is nen bsp mit MessageBoxA aus der user32.dll
anstatt den text 'hallo :)' auszugeben wird 'hallo :) hooked!!!' ausgegeben
wenn jemand genauer daran interessiert wie der hook funzt kann ich ihm das erklären :)
Sprint - Do 26.08.04 16:19
Hallo,
beschäfitge mich auch gerade mit dem hooken von API's. Hab unter anderem
madCodeHook [
http://www.madshi.net/],
afxCodeHook [
http://www.iamaphex.net] und die HookImport Funktion aus dem
JEDI Project [
http://www.delphi-jedi.org/] getestet.
Ich finde das madCodeHook am stabilsten läuft. Naja, aber US $125 wollte ich eigentlich nicht ausgeben. Und ich möchte natürlich auch, das ich meine verwendeten Komponenten, als Quellcode hab.
Ich finde das gut, das du hier eine OpenSource Möglichkeit gepostet hast.
Ich suche noch ein Beispiel, wie ich meine DLL in ein Prozess injecten kann.
Zurzeit benutze ich CreateRemoteThread. Das funktioniert unter NT-basierten Systerm auch sehr gut. Nur auf Win9x Rechnern geht das nicht.
Also wenn du da noch irgendwo 'nen Quelltext liegen hast... Das würde mich sehr freuen, wenn du den hier auch noch veröffentlich kannst. :D
uall@ogc - Do 26.08.04 16:30
uall@ogc - Do 26.08.04 17:19
naja ich beschreib mal was der apiHook (in meinem #1 post von dem thread) genau macht / was für probleme aufgetreten sind usw.
zuerst wird speicher reserviert
Delphi-Quelltext
1: 2: 3: 4: 5:
| push PAGE_EXECUTE_READWRITE push MEM_COMMIT or MEM_RESERVE push 64+5*4 push 0 call VirtualAlloc |
danach wird eine funktion in den speicher geschrieben:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16:
| mov [eax], $C7241C8B; add eax, 4 mov [eax], $00002404; add eax, 4 mov [eax], $058b0000; add eax, 4 mov [eax], $00000000; add eax, 4 mov [eax], $0000158B; add eax, 4 mov [eax], $10890000; add eax, 4 mov [eax], $0000158B; add eax, 4 mov [eax], $50890000; add eax, 4 mov [eax], $50E0FF04; add eax, 4 mov [eax], $00058B52; add eax, 4 mov [eax], $8B000000; add eax, 4 mov [eax], $00000015; add eax, 4 mov [eax], $8B108900; add eax, 4 mov [eax], $00000015; add eax, 4 mov [eax], $04508900; add eax, 4 mov [eax], $E3FF5A58; add eax, 4 |
später zu der funktion mehr :)
nachdem wir die funktion in den speicher geschriben haben müssen wir noch nen paar adresse patchen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28:
| mov ebx, eax add eax, 6 add ebx, $23 mov [eax], ebx
add eax, 6 add ebx, $40-$23 mov [eax], ebx
add eax, 6 add ebx, 4 mov [eax], ebx
add eax, 8 add ebx, 4 mov [eax], ebx
add eax, 13 sub ebx, 8 mov [eax], ebx
add eax, 6 add ebx, 12 mov [eax], ebx
add eax, 8 add ebx, 4 mov [eax], ebx |
dann wird VirtualProtect auf die original funktion aufgerufen um dort schreib und leserechte zu bekommen und unseren jmop zu setzen
Delphi-Quelltext
1: 2: 3: 4: 5: 6:
| push eax push esp push PAGE_EXECUTE_READWRITE push 8 push [old] call VirtualProtect |
jetzt speicher wir die 8 bytes die an der funktion stehen (die später mit einem jp überschriben werden)
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| push eax add eax, $40 mov ebx, [old] mov [eax], ebx add eax, 4 mov ecx, [ebx] mov [eax], ecx add ebx, 4 add eax, 4 mov ecx, [ebx] mov [eax], ecx |
dann schreiben wir den jmp zu unserer funktion
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| mov eax, [old] mov byte ptr [eax], $E9 add eax, 1 mov ebx, [my] sub ebx, [old] sub ebx, 5 mov [eax], ebx |
und speicher dann diesen auch nochmal ab
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| add eax, $40 add eax, 12 mov ebx, [old] mov ecx, [ebx] mov [eax], ecx add eax, 4 add ebx, 4 mov ecx,[ebx] mov [eax], ecx |
zum schluss wird die NEXT funktion gesetzt, diese liegt ja an dem speicher den wir reserviert haben und mit unserer funktion beschriben haben
Delphi-Quelltext
1: 2: 3:
| mov eax, [next] pop edx mov [eax], edx |
sinn des api hooks ist es das ein jmp an $E9 xx xx xx xx an den beginn der API geschriebn wird und dieser jmp zu der eigen funktion springt, das ist eigentlich kein problem, nur wenn man die original funktion wieder aufrüfen würde, ensteht eine endlosschleife da der jmp immer wieder ausgeführt wird
also muss man den code bevor man die original funktion aufruft wieder herstellen
dies macht die funktion nextMessageBoxA
nun schaunen wir uns diese mal genauer an, wir haben sie an den reservierten speicher geschrieben
diese funktion sieht im speicher folgendermaßen aus:
[img]
http://www.arschvoll.net/apihook01.jpg
[/img]
diese funktion ist die "nextMessageBoxA" funktion und man kann sie in delphi auch so schreiben:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38:
| function nextMessageboxA(hWnd: HWND; lpText, lpCaption: pchar; uType: cardinal): UINT; stdcall; asm pop ebp mov ebx, [esp] mov [esp], offset @lala
mov eax, [offset @save] mov edx, [offset @save+4] mov [eax], edx mov edx, [offset @save+8] mov [eax+4], edx
jmp eax @lala:
push eax push edx mov eax, [offset @save] mov edx, [offset @save+12] mov [eax], edx mov edx, [offset @save+16] mov [eax+4], edx pop eax pop edx
jmp ebx @save: fnop; fnop fnop; fnop fnop; fnop fnop; fnop fnop; fnop end; |
bei @save werden folgende werte gespeichert:
1.) 4 bytes für die addresse wo die original API funktion beginnt
2.) 2*4 bytes für die original daten die an der funktion stehen
3.) 2*4 bytes fpr den jmp den man an die original funktion setzt
wenn nun die nextMessageBoxA funktion aufgerufen wird, werden die 8 originalen bytes an die "oldMessageBoxA" funktion geschrieben und diese funktion wird dann aufgerufen
hier gab es nun ein problem, und zwar wenn man die original funktion callt, liegt aufm dem stack als 1. die return adresse und die ganzen parameter (im bsp sind es 4)
das programm ruft die original funktion auf:
auf dem Stack liegt dann
4.parameter
3.parameter
2.parameter
1.parameter
return adresse
nun haben wir ja bei der original funktion einen jmp gesetzt dieser springt zu unserer funktion
die funtkion macht dann irgendwas und will nun NEXT funktion aufrufen
also legt unsere funktion wiederum alle parameter + return adresse auf den stack
4.parameter
3.parameter
2.parameter
1.parameter
return adresse - bei dem aufruf der orig funktion
4.parameter
3.parameter
2.parameter
1.parameter
return adresse - irgendwo in meiner funktion
jetzt sind wir in der next funktion, diese soll ja den jmp bei der originalen funktion entfernen um dann wiederum die originale funktion aufzurufen
das entfernen des jumps ist nicht das problem, aber wird müssen nun entweder alle parameter wieder auf den stack legen um die originale funktion zu callen, wenn wir das nicht machen würden, würde der stack nach dem call so aussehen:
4.parameter
3.parameter
2.parameter
1.parameter
return adresse - bei dem aufrud der orig funktion
4.parameter
3.parameter
2.parameter
1.parameter
return adresse - irgendwo in meiner funktion
return adresse - irgendwo in der next funktion
die parameter liegen alle falsch :/
nun kennen wir aber nicht die anzahl der parameter, aber wir wissen das ein
call nichts anderes macht als:
push eip + 10
jmp function
(5 bytes für push adresse und 5 für den jmp / bei nem call würde nur EIP+5 aufn stack gelegt)
wie könne wir das nun nutzen?
wir wissen das
4.parameter
3.parameter
2.parameter
1.parameter
return adresse - bei dem aufrud der orig funktion
4.parameter
3.parameter
2.parameter
1.parameter
return adresse - irgendwo in meiner funktion
auf dem stack liegt, also warum ändern wir nicht einfach die 2. return adresse ab
und springen einfach zu der originalen funktion?
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| mov [esp], offset @lala return adresse abändern (neue return bei lala)
mov eax, [offset @save] unhooken der funktion (jmp removen) mov edx, [offset @save+4] mov [eax], edx mov edx, [offset @save+8] mov [eax+4], edx
jmp eax zu der original funktion springen @lala: hier landen wir dann nach dem jmp |
nachdem wir nun die original funktion aufgerufen haben müssen wir natürlich wieder den jmp hinsetzen damit wir bei einem 2. aufruf auch wieder den call anfangen können:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| push eax push edx mov eax, [offset @save] mov edx, [offset @save+12] mov [eax], edx mov edx, [offset @save+16] mov [eax+4], edx pop eax pop edx
jmp ebx |
wichtig dabei ist es, EAX zu sichern, falls es sich bei der gehookten API um eine funktion handelt die einen rückgabewert hat (dieser steht immer in EAX)
schließlich müssen wir noch zu der retrun adresse zurücksprignen die wir überschriben haben
is bisl kompliziert, wer net so dne plan von asm hatwird da wohl auch net durchblicken aber für diejenigen dies könen nisses sehr interessant
uall@ogc - Fr 27.08.04 14:49
neue version des API hooks
kleine verbesserungen, return adresse wird nicht mehr in EBX gespeichert, funzt nun auch bei APIs die EBX verändern, ausserdem wurde aus der procedure eine funktion gemacht die bei fehlern beim hook false zurückgibt
wäre nett wenn die funktion genutzt das man mir das mitteilt ;>
ausserdem wünsch ich mir das mal jemand hier antwortet damit ich weiß das überhapt interesse daran besteht THX
EDIT: hab nen kleinen bug behoben das immer TRUE zurückgegeben wurde
und hab noch ne UnhookAPI funktion geschrieben bei der man einfach NEXT funktion angeben muss
diese gibt auch TRUE/FALSE zurück wenns das unhooken funzt oder net
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178:
| function HookApi(my, old: pointer; var next: pointer): boolean; stdcall; assembler; asm push PAGE_EXECUTE_READWRITE push MEM_COMMIT or MEM_RESERVE push $4C+6*4 push 0 call VirtualAlloc cmp eax, 0 je @endef
mov ebx, eax mov [ebx], $8924048B; add ebx, 4 mov [ebx], $00000005; add ebx, 4 mov [ebx], $2404c700; add ebx, 8 mov [ebx], $0000058b; add ebx, 4 mov [ebx], $158b0000; add ebx, 4 mov [ebx], $00000000; add ebx, 4 mov [ebx], $158b1089; add ebx, 8 mov [ebx], $ff045089; add ebx, 4 mov [ebx], $8b5250e0; add ebx, 4 mov [ebx], $00000005; add ebx, 4 mov [ebx], $00158b00; add ebx, 4 mov [ebx], $89000000; add ebx, 4 mov [ebx], $00158b10; add ebx, 4 mov [ebx], $89000000; add ebx, 4 mov [ebx], $5a580450; add ebx, 4 mov [ebx], $000025ff; mov ecx, eax; add ecx, 5 mov ebx, eax; add ebx, $4a+20 mov [ecx], ebx
mov ecx, eax; add ecx, 12 mov ebx, eax; add ebx, $29 mov [ecx], ebx
mov ecx, eax; add ecx, 18 mov ebx, eax; add ebx, $4a+0 mov [ecx], ebx
mov ecx, eax; add ecx, 24 mov ebx, eax; add ebx, $4a+4 mov [ecx], ebx
mov ecx, eax; add ecx, 32 mov ebx, eax; add ebx, $4a+8 mov [ecx], ebx
mov ecx, eax; add ecx, 45 mov ebx, eax; add ebx, $4a+0 mov [ecx], ebx
mov ecx, eax; add ecx, 51 mov ebx, eax; add ebx, $4a+12 mov [ecx], ebx
mov ecx, eax; add ecx, 59 mov ebx, eax; add ebx, $4a+16 mov [ecx], ebx
mov ecx, eax; add ecx, 70 mov ebx, eax; add ebx, $4a+20 mov [ecx], ebx
mov ebx, eax push eax push esp push PAGE_EXECUTE_READWRITE push 8 push [old] call VirtualProtect pop ecx cmp eax, 0 je @endef
mov eax, ebx push eax add eax, $4a mov ebx, [old] mov [eax], ebx add eax, 4 mov ecx, [ebx] mov [eax], ecx add ebx, 4 add eax, 4 mov ecx, [ebx] mov [eax], ecx
mov eax, [old] mov byte ptr [eax], $E9 add eax, 1 mov ebx, [my] sub ebx, [old] sub ebx, 5 mov [eax], ebx
pop eax push eax
add eax, $4a add eax, 12 mov ebx, [old] mov ecx, [ebx] mov [eax], ecx add eax, 4 add ebx, 4 mov ecx,[ebx] mov [eax], ecx
mov eax, [next] pop edx mov [eax], edx mov eax, 1 pop ebp ret 3*4
@endef: xor eax, eax pop ebp ret 3*4 end;
function UnhookApi(next: pointer): boolean; stdcall; assembler; asm push eax push esp push PAGE_EXECUTE_READWRITE push $4C+6*4 push [next] call VirtualProtect pop ebx cmp eax, 0 je @endef cmp ebx, PAGE_EXECUTE_READWRITE jne @endef
mov eax, [next] add eax, $4a mov ebx, [eax]
push eax push ebx
push ecx push esp push PAGE_EXECUTE_READWRITE push 8 push ebx call VirtualProtect pop ecx cmp eax, 0 je @endef
pop ebx pop eax
cmp byte ptr [ebx], $E9 jne @endef
add eax, 4 mov ecx, [eax] mov [ebx], ecx add eax, 4 add ebx, 4 mov ecx, [eax] mov [ebx], ecx
mov eax, 1 pop ebp ret 4 @endef: xor eax, eax pop ebp ret 4 end; |
opfer.der.genauigkeit - Fr 27.08.04 17:26
Ziemlich geniale Sache.
Wenn ich mal n bißchen Zeit habe, kann ich mir das genauer reinziehen und meine
Assemblerkenntnisse verbessern.. who knows ;)
BenBE - Fr 27.08.04 20:15
Kleiner Tipp:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38:
| push PAGE_EXECUTE_READWRITE push MEM_COMMIT or MEM_RESERVE push $4C+6*4 push 0 call VirtualAlloc TEST EAX, EAX je @endef
MOV EDI, EBX LEA ESI, @@ASMRoutinenSource MOV ECX, @@ASMRoutinenSource_Ende - @@ASMRoutinenSource REPNZ MOVSB
LEA EBX, [EAX + $4a + 20] MOV DWORD PTR [EAX + $0000005], EBX ...
JMP @@Ret
@@ASMRoutineenSource: @@ASMRoutineenSource_Ende:
@@Ret:
end; |
Damit ist dein Source etwas einfacher zu Verstehen, da die wenigsten Opcodes aus dem Kopf interpretieren können.
uall@ogc - Fr 27.08.04 20:35
jo is dann wirklich bisl einfacher zu verstehen, ich kann das ja mal so umbauen, glaub zwar eh das kaum jemand daran interessiert sit zu gucken ob das funzt sondern die meisten das einfach benutzen wollen
aber thx für info BenBE, ich werds ändern
uall@ogc - Sa 28.08.04 13:11
hab hier nochmal ne verbesserte funktion (nu hook funktion nicht unhook)
hab mal comments beigeschriebn damit man sich nen überblick über die funktions weise machen kann
bevor jemand was dazu postet:
ich weiß das man sich das
pop eax; push eax
sparen kan nwenn man einfach nen anderes register nimmt
oder man z.b. net jedes mal die größe der NEXT-funkton berechnen muss
werde das auch noch ändern
zu BenBE: die berechnungne mit LEA funktionieren nicht wenn man 2 labels angibt :/
EDIT: hab auch kommentare zu UnhookApi geschreiben, diese gibt nun auch wieder den speicher frei, desweiteren werden bei HookApi in der Next-funktion nun auch vor dem aufruf der original api alle register gespeichert, und der jmp gfeht nicht mer per jmp EAX sondern wird vorher auch noch in eine variabel geschrieben
EDIT2: nen bug bei der UnhookApi funktion entfernt... ret 4 anstatt ret 4*3 weil nur 1 parameter, hatte aus der HookApi kopiert aber nicht geändert...
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178:
| function HookApi(my, old: pointer; var next: pointer): boolean; stdcall; assembler; const varcount = 6; asm mov ecx, offset @@endasmfunction sub ecx, offset @@beginasmfunction push ecx add ecx, varcount*4 push PAGE_EXECUTE_READWRITE push MEM_COMMIT or MEM_RESERVE push ecx push 0 call VirtualAlloc test eax, eax pop ecx je @endef mov edi, eax mov esi, offset @@beginasmfunction repz movsb mov ebx, eax add ebx, offset @@endejmp sub ebx, offset @@beginasmfunction mov [EAX + 16], ebx mov ebx, eax add ebx, offset @@endasmfunction sub ebx, offset @@beginasmfunction add [EAX + 8], ebx add [EAX + 22], ebx add [EAX + 28], ebx add [EAX + 36], ebx add [EAX + 47], ebx add [EAX + 55], ebx add [EAX + 61], ebx add [EAX + 69], ebx add [EAX + 80], ebx mov ebx, eax push ecx push esp push PAGE_EXECUTE_READWRITE push 8 push [old] call VirtualProtect pop ecx cmp eax, 0 je @endef mov eax, ebx push eax add eax, offset @@endasmfunction sub eax, offset @@beginasmfunction mov ebx, [old] mov [eax], ebx add eax, 4 mov ecx, [ebx] mov [eax], ecx add ebx, 4 add eax, 4 mov ecx, [ebx] mov [eax], ecx mov eax, [old] mov byte ptr [eax], $E9 add eax, 1 mov ebx, [my] sub ebx, [old] sub ebx, 5 mov [eax], ebx pop eax push eax add eax, offset @@endasmfunction sub eax, offset @@beginasmfunction add eax, 12 mov ebx, [old] mov ecx, [ebx] mov [eax], ecx add eax, 4 add ebx, 4 mov ecx,[ebx] mov [eax], ecx mov eax, [next] pop edx mov [eax], edx mov eax, 1 pop ebp ret 3*4 @endef: xor eax, eax pop ebp ret 3*4 @@beginasmfunction: push eax push edx mov eax, [esp+8] mov [$00000000+20], eax mov [esp+8], $00000000 mov eax, [$00000000+0] mov edx, [$00000000+4] mov [eax], edx mov edx, [$00000000+8] mov [eax+4], edx pop edx pop eax jmp [$00000000+0] @@endejmp: push eax push edx mov eax, [$00000000] mov edx, [$00000000+12] mov [eax], edx mov edx, [$00000000+16] mov [eax+4], edx pop eax pop edx jmp [$00000000+20] @@endasmfunction: end; |
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139:
| function UnhookApi(next: pointer): boolean; stdcall; assembler; const varcount = 6; asm mov ebx, offset @@endasmfunction sub ebx, offset @@beginasmfunction add ebx, 4*varcount push eax push esp push PAGE_EXECUTE_READWRITE push ebx push [next] call VirtualProtect pop ebx cmp eax, 0 je @endef cmp ebx, PAGE_EXECUTE_READWRITE jne @endef mov ebx, offset @@endasmfunction sub ebx, offset @@beginasmfunction mov eax, [next] add eax, ebx mov ebx, [eax] push eax push ebx push ecx push esp push PAGE_EXECUTE_READWRITE push 8 push ebx call VirtualProtect pop ecx test eax, eax je @endef pop ebx pop eax cmp byte ptr [ebx], $E9 jne @endef add eax, 4 mov ecx, [eax] mov [ebx], ecx add eax, 4 add ebx, 4 mov ecx, [eax] mov [ebx], ecx push MEM_RELEASE push 0 push [next] call VirtualFree mov eax, 1 pop ebp ret 4 @endef: xor eax, eax pop ebp ret 4 @@beginasmfunction: push eax push edx mov eax, [esp+8] mov [$00000000+20], eax mov [esp+8], $00000000 mov eax, [$00000000+0] mov edx, [$00000000+4] mov [eax], edx mov edx, [$00000000+8] mov [eax+4], edx pop edx pop eax jmp [$00000000+0] @@endejmp: push eax push edx mov eax, [$00000000] mov edx, [$00000000+12] mov [eax], edx mov edx, [$00000000+16] mov [eax+4], edx pop eax pop edx jmp [$00000000+20] @@endasmfunction: end; |
BenBE - Sa 28.08.04 23:08
Warum nutzt du in der Unhook-Funktion im Displacement $00000000 als Basis-Adresse? Ich denk mal, das wird zu ner AV führen ... (Oder welchen Grund gibt es dafür beim Unhooken das System zu crashen???)
Ooops, hat sich erledigt, hab übersehen, dass die Routine in der Unhook-Funktion nicht geschrieben werden. Self-Modifying Code ist aber auch immer Kompliziert :D :oops:
uall@ogc - So 29.08.04 12:12
nene beim unhook ist die funktion nur zum berechnen der größe da, damit ich weiß wo die variablen beginnen (di stehen hinter der funktion die in in den reservierten speicher kopiere) die erste variable ist die adresse der original API wo ich den JMP hinsetze, diese lese ich aus schau ob der jmp da ist und überschreibe das einfach mit den originalen 8 bytes (5 bytes für den jmp) dann gebe ich den reservierten speicher wieder frei, die funktion mit den $00000000 rufe ich nie auf, sie ist halt nur dafür da um zu berechnen wo die variablen beginnen :)
BenBE - So 29.08.04 20:28
Beim Studieren des Codes von afx ist mir noch ne Idee gekommen, wie du nur einmal den Code für Hook und Unhook brauchst und trotzdem einfach die Größe berechnen kannst:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| procedure HookCode;assembler; asm end;
procedure HookCodeEnd;assembler; asm; end; |
Die Größe erhälst du dann mit DWORD(@HookCodeEnd) - DWORD(HookCode) und den HookCode selber mit @HookCode.
in ASM also:
LEA ECX, [HookCodeEnd]
LEA ESI, [HookCode] //Offset
SUB ECX, ESI //Größe
uall@ogc - So 29.08.04 20:54
Delphi-Quelltext
1:
| DWORD(@HookCodeEnd) - DWORD(HookCode) |
fehlt nen @ = DWORD(@HookCode)
oder man nimmt
Delphi-Quelltext
1: 2:
| MOV ECX, offset HookCodeEnd SUB ECX, offset HookCode |
;>
BenBE - So 29.08.04 21:33
Wobei die Möglichkeit über LEA den Vorteil hat, dass schon alles (Größe und Offset) in den Richtigen Registern stehen.
BenBE - Mi 08.09.04 22:28
Ich wollte mal, weil mir das nicht so richtig klar geworden ist, anfragen, in wie weit es Einschränkungen bei den API Hooks gibt, weil bestimmte Berechtigungen erforderlich sind?
Also, z.B. funzt das als Otto Normal-User ohne Extra-Privilegien?
(P.S.: Das es mit Umwegen funzt, wenn man den SMC steuern kann, hat Assabad ja schon bewiesen ;-), das mein ich aber nicht)
Ich mein im Speziellen, ob ein normaler User, der Benutzer-Gruppe, sich z.B. in den Explorer, oder PID 8 einklinken könnte, d.h. in Prozesse auch von anderen Usern.
TIA.
Sprint - Mi 08.09.04 23:01
| BenBE hat folgendes geschrieben: |
| Ich mein im Speziellen, ob ein normaler User, der Benutzer-Gruppe, sich z.B. in den Explorer, oder PID 8 einklinken könnte, d.h. in Prozesse auch von anderen Usern. |
Ich weiß jetzt nicht was du mit PID 8 meinst. Aber beim Explorer und so, da kannst du dich so auch ganz leicht einklicken.
Zum Beispiel installierst z.B. ein MsgHook systemweit und wenn deine DLL in Prozess vom Explorer geladen wird, kannst du dort die API's gegen deine Funktionen austauschen.
BenBE - Do 09.09.04 09:28
| Sprint hat folgendes geschrieben: |
| BenBE hat folgendes geschrieben: | | Ich mein im Speziellen, ob ein normaler User, der Benutzer-Gruppe, sich z.B. in den Explorer, oder PID 8 einklinken könnte, d.h. in Prozesse auch von anderen Usern. |
Ich weiß jetzt nicht was du mit PID 8 meinst. |
PID 8 ist der System Prozess (Kernel) von Windows 2000.
| Sprint hat folgendes geschrieben: |
Aber beim Explorer und so, da kannst du dich so auch ganz leicht einklicken.
Zum Beispiel installierst z.B. ein MsgHook systemweit und wenn deine DLL in Prozess vom Explorer geladen wird, kannst du dort die API's gegen deine Funktionen austauschen. |
Schon klar, aber ich glaub nicht, dass der Explorer meine DLL ohne Grund laden wird ;-) Und außerdem wolte ich in einen anderen Prozess rein, der nicht durch ein paar simple RegKeys dazu gebracht werden kann, eine DLL zu laden...
Und das es als Admin geht, weiß ich ... Jedoch nicht, ob's auch als Normal-User geht ...
Sprint - Do 09.09.04 12:59
| BenBE hat folgendes geschrieben: |
| Schon klar, aber ich glaub nicht, dass der Explorer meine DLL ohne Grund laden wird ;-) |
Auf NT-basierten Betriebssystemen kannst du ja auch CreateRemoteThread benutzen. Das funktioniert wunderbar.
Und mit einem systemweitern MsgHook (SetWindowsHookEx) wird deine DLL auch in den Prozess des Explores geladen.
| Zitat: |
| Und außerdem wolte ich in einen anderen Prozess rein, der nicht durch ein paar simple RegKeys dazu gebracht werden kann, eine DLL zu laden... |
Weiß ich nicht was du damit meinst? Von RegKeys habe ich nicht geredet. Geht ja auch nur, wenn die Anwendug die user32dll geladen hat. So macht das z.B. Window Blinds.
uall@ogc - Do 09.09.04 16:22
@BenBE
suchst vill sowas:
function GetDebugPrivilege: boolean;
{unser prozess bekomm debug previlegien,
benötigt um einen prozess handle von einem systemprozess zu bekommen}
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| function GetDebugPrivilege: boolean; var hToken,rel: cardinal; tkp: TOKEN_PRIVILEGES; luid: int64; begin result := false; if OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken) then begin if LookupPrivilegeValue(nil, 'SeDebugPrivilege', luid) then begin tkp.PrivilegeCount := 1; tkp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED; tkp.Privileges[0].Luid := luid; result := AdjustTokenPrivileges(hToken, FALSE, tkp, sizeof(tkp), nil, rel); end; CloseHandle(hToken); end; end; |
BenBE - Do 09.09.04 16:59
Ne, das Debug-Priviledge ist nur für Admins verfügbar (oder wenn man's anderen Usern gibt. Bei Delphi funzt das Debuggen auch nur, da die Prozesse die Security-Einstellungen der IDE erben und damit die IDE das Recht hat drauf zuzugreifen.
Um meine Frage mal umzuformulieren: Ich suche was, was es mir erlaubt, die Funktionalität von Assabad's CMD_SVC direkt über nen DLL Injection auszuführen.
Hintergrund: Möchte paar Klassenkammeraden mal etwas verarschen, indem ihre Rechner ferngesteuert runterfahren. Funzt als Admin auf meinem Sys auch ganz fein, jedoch nur, wenn ich mich auf dem Remote-System als Admin authentifiziere (oder anderer Account mit RemoteShutdownPriv) -> Ich muss lokal einen Account haben, der bei Remote-Anmeldung die SeRemoteShutdownPriv hat. Und das ist eben entweder LocalSystem oder Admin. Und da ich den Admin-Account nicht knacken will oder sonstewie die Konfig ändern darf, bleibt nur der Weg über die System-Prozesse.
BenBE - Fr 10.09.04 20:28
Also gut, hab nochmal einen anderen Ansatz versucht, der theoretisch funktionieren sollte, jedoch an den Speicher-Berechtigungen scheitert.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124:
| Function HookApi(my, old: Pointer; Var Next: Pointer): Boolean; Stdcall; Assembler; Asm MOV EAX, DWORD PTR [Old] CMP WORD PTR [EAX], $25FF JNZ @@RetFailure
MOV ECX, OFFSET @@ASMHookLoader_End SUB ECX, OFFSET @@ASMHookLoader_Start
PUSH ECX
PUSH PAGE_EXECUTE_READWRITE PUSH MEM_COMMIT or MEM_RESERVE PUSH ECX PUSH $00000000 CALL VirtualAlloc
POP ECX TEST EAX, EAX JZ @@Ret MOV EDI, EAX LEA ESI, @@ASMHookLoader_Start REPNZ MOVSB MOV EDI, EAX MOV ESI, DWORD PTR [Old] MOV ECX, OFFSET @@ASMHookLoader_OldProc SUB ECX, OFFSET @@ASMHookLoader_Start MOV EDX, DWORD PTR [ESI+$00000002] MOV DWORD PTR [ESI+$00000002], EAX MOV DWORD PTR [EDI+ECX], EDX MOV ECX, OFFSET @@ASMHookLoader_ProcPtr SUB ECX, OFFSET @@ASMHookLoader_Start MOV EDX, DWORD PTR [My] MOV DWORD PTR [EDI+ECX], EDX MOV ECX, OFFSET @@ASMHookLoader_JumpInstruction SUB ECX, OFFSET @@ASMHookLoader_Start LEA EDX, DWORD PTR [EDI+ECX] MOV DWORD PTR [Next], EAX PUSH ERROR_SUCCESS CALL SetLastError
XOR EAX, EAX INC EAX JMP @@Ret
@@ASMHookLoader_Start: PUSH EAX CALL @@ASMHookLoader_GetMemPos @@ASMHookLoader_Relative: ADD EAX, OFFSET @@ASMHookLoader_ProcPtr SUB EAX, OFFSET @@ASMHookLoader_Relative MOV EAX, DWORD PTR [EAX] XCHG EAX, [ESP] RET @@ASMHookLoader_JumpInstruction: PUSH EAX CALL @@ASMHookLoader_GetMemPos @@ASMHookLoader_OldRelative: ADD EAX, OFFSET @@ASMHookLoader_OldProc SUB EAX, OFFSET @@ASMHookLoader_OldRelative MOV EAX, DWORD PTR [EAX] XCHG EAX, [ESP] RET @@ASMHookLoader_ProcPtr: DD $00000000 @@ASMHookLoader_OldProc: DD $00000000 @@ASMHookLoader_GetMemPos: POP EAX PUSH EAX RET @@ASMHookLoader_End:
@@OldLibraryProtection: DD $00000000 @@RetProtectionError: PUSH ERROR_ACCESS_DENIED CALL SetLastError XOR EAX, EAX JMP @@Ret
@@RetFailure: PUSH ERROR_NOT_SUPPORTED CALL SetLastError XOR EAX, EAX @@Ret: End; |
Die Auskommentierten Zeilen sind meine Versuche, Windows doch dazu zu bewegen, mir Schreibzugriff auf DWORD PTR [ESI+$02] zu geben, die aber leider Erfolglos blieben.
Die Routine funktioniert nur mit statisch importierten Funktionen (wenn es funzen würde) und behebt ein Problem vom oben aufgeführten API-Hook von uall@ogc, der bei diesen Funktionen zu AccessViolations führt (zumindest bei mir):
Testcode mit meinem Versuch GetLastError etwas Windows-Gerechter zu Schreiben
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| procedure TForm1.Button1Click(Sender: TObject); type TGetLastErrorProc = function:DWORD;stdcall; var OldGetLastErr:TGetLastErrorProc; function MyGetLastError:DWORD;stdcall; begin Result := Random(65536); end; begin HookApi(@MyGetLastError, @GetLastError, @OldGetLastErr); Caption := IntToStr(GetLastError); UnHookApi(@OldGetLastErr); end; |
AV kommt, wenn Delphi versucht mit Call TComponent.SetCaption den Text zuzuweisen. Außerdem wird im Import-Modul die nächste Funktion durch die ASM-Routine überschrieben.
Wäre dankbar, wenn sich das mal jemand angucken könnte, um mir Tips zu geben, was ich vor dem Zugriff auf den LibPtr noch beantragen muss.
uall@ogc - Sa 11.09.04 11:35
Naja zu deiner Methode:
1. hat sie mit meiner fast net mehr soviel gemeinsam, deine kann "nur" statische imports hooken, meine sollte (funzt in deinem bsp net) alle APIs hooken können :)
2. crasht deine routine bei mir genauso wie meine ^^
3. solltest auf die OLD funktion am anfang nen virtualprotect machen, kann ja sein das jemand die lese/schreibrechte auf die page genommen hat
4. wünsch ich mir mal nen ASM forum
5. wenn ich falsch liegen sollte dann plz schreiben! ;>
EDIT:
naja wenn ich mir zuerst mal deinen text dazu durchgelesen hätte und ent auf die funktion geguckt hätte, wüßte ich das du shcon auf punkt 1/2 eingegangen bist - *aufschlägewart*
EDIT2:
gnaa wasn das bidde?
wie willst denn auf nen reservierten speicher nochmal den speicher reservieren?
willst net lieber VirtualProtect nehmen? ;)
btw. solltest das schon direkt am anfang machen bevor du überhaupt guckst ob die old funktion nen statischer import ist!
BenBE - Sa 11.09.04 18:47
Werd mir das mit dem VirtualProtect mal ansehen. Meld mich evtl. nochmal.
uall@ogc - So 12.09.04 14:40
nur zur info:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| procedure _exception; var p: pointer; old: cardinal; b: ^byte; begin p := virtualalloc(nil,$1000,MEM_COMMIT OR MEM_RESERVE,PAGE_NOACCESS); b := pointer(integer(p)+10); b^ := $50; end; |
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| procedure _noexception; var p: pointer; old: cardinal; b: ^byte; begin p := virtualalloc(nil,$1000,MEM_COMMIT OR MEM_RESERVE,PAGE_NOACCESS); virtualprotect(p,6,PAGE_EXECUTE_READWRITE,old); b := pointer(integer(p)+10); b^ := $50; end; |
Kedariodakon - Mi 19.01.05 10:38
morgen :D
Wollte mich mal bedanken für den souce...
Bin gerade dabei das alles zu verstehen :shock:
Aber frage mich gerade, was die Funktion überhaupt bekommt, da sie ja 3 Addressen will...
Delphi-Quelltext
1:
| function HookApi(my, old: pointer; var next: pointer): boolean; stdcall; assembler; |
Und die zum unhooken will auch eine :roll:
Delphi-Quelltext
1:
| UnhookApi(next: pointer): boolean; stdcall; assembler; |
Was bekommen die da übergeben?
Brauch ich noch mehr als die beiden?
Arbeite bisjetzt nur mit der Mad-Komponente, da is das ein klein wenig anders...
Möchte sie aber gern gegen Source ersetzen...
Bin leider kein Licht in Assembeler...
Wie testet ihr die Funktionen? Runtime-debugger? könnt ihr da einen empfehlen?
Hatte früher mal Softice gehabt, war net schlecht, glaub ich zumindest ^^
Aber was nimmt man heute zu WinXP-SP2 Zeiten?
Bye
BeautifulRuby - Do 20.01.05 01:53
Kann mir einer ein Beispiel geben, wie man Systemweit ein Hook macht?
Einfach eine veränderte MessageBox-Funktion, wo egal wer MessageBox aufruft, der Inhalt verändert dargestellt wird, also aus "Wollen Sie die Datei speichern?" wird "Wollen Sie die Datei speichern? - TEST!"
Die Demos die ich im Internet gefunden habe, funktionierten bei mir nicht: entweder brauchte man fremdkomponente oder Unit fehlte oder auch System stürzte ab.
Delete - Do 20.01.05 02:46
| BenBE hat folgendes geschrieben: |
Ne, das Debug-Priviledge ist nur für Admins verfügbar (oder wenn man's anderen Usern gibt. Bei Delphi funzt das Debuggen auch nur, da die Prozesse die Security-Einstellungen der IDE erben und damit die IDE das Recht hat drauf zuzugreifen.
|
Und woher erbt das Token des IDE-Prozesses das Privileg? Richtig, von der Logon-Session. Und wenn du als einfacher Benutzer angemeldet bist, läuft die IDE mit deinen Rechten und Privilegien. Und eigene Prozesse darfst du immer debuggen. Ich kann nämlich sehr wohl als benutzer mit dem Delphi Debugger meine Anwendungen debuggen. Mit dem Debug-Privileg darfst du das eben auch für Prozesse, die nicht von dir sind.
uall@ogc - Do 20.01.05 13:40
@Kedariodakon:
function HookApi(my, old: pointer; var next: pointer): boolean; stdcall; assembler;
my = eigene funktion die aufgerufen werden soll
old = funktion die gehookt werden soll
next = neue funktion die man in der my funktion aufrufen kann
bsp:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| var oldMessageBoxA: function (i: integer; a,b: pchar; j: integer): integer; stdcall; nextMessageBoxA: function(i: integer; a,b: pchar; j: integer): integer; stdcall;
function myMessageBoxA(i: integer; a,b: pchar; j: integer): integer; stdcall; begin result := nextMessageBoxA(i,pchar(a+' test'),b,j); end;
procedure InstallHook; begin @oldMessageBoxA := getprocaddress(getmodulehandle('user32.dll'),'MessageBoxA'); HookAPI(@myMessageBoxA,@oldMessageBoxA,@nextMessageBoxA); end; |
bei der oldMessageBoxA wird ein jump gesetzt zu myMessageBoxA und damit keine endlosscheife auftritt muss man nextMessageBoxA aufrufen
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!