MidFunction Hooking

    Publicités

Users Who Are Viewing This Thread (Total: 0, Members: 0, Guests: 0)

Primker

Nouveau membre
May 4, 2015
1
1
61
44
Dans ce tuto on va voir assez rapidement comment les hooks au milieu d'une fonction (plus communément appelés "Mid-Function Hooks" marchent et comment en coder un basique.

Préreq: AssaultCube (la base, v1.1.0.4 de préférence), savoir utiliser Olly un minimum, bases en C++ et ASM - pas besoin de CE.

Tout d'abord qu'est-ce que c'est? Pour rester simple dans l'explication, à un endroit donné du code on va exécuter le nôtre, puis "repasser le contrôle" au programme.
J'ai choisi AC pour ce tuto car beaucoup d'adresses sont postées sur le net. Notre but va d'être de hooker l'endroit où l'ammo baisse pour dire au jeu de l'augmenter. En gros au lieu de perdre une balle on en ""gagnera" une. :)

L'instruction qui fait baisser l'ammo se trouve @0x45B75F (j'utilise AC v1.1.0.4). Je ne vais pas expliquer comment on trouve ça, deux trois scans + quelles instructions modifient l'adresse et vous l'avez. Ce n'est pas le but du tuto. :p

Donc dans Olly à l'adresse 0x45B75F on a ça:
0045B75F FF 0E DEC DWORD PTR DS:[ESI]


0045B75F => Adresse virtuelle (ne change JAMAIS, à part si l'exécutable du jeu est mise à jour)
FF 0E => Opcodes
DEC DWORD PTR DS:[ESI] => La traduction des opcodes en language ASM (Intel).

Ce qui veut dire... à l'adresse 0045B75F se trouve l'instruction DEC DWORD PTR DS:[ESI] qui fait baisser l'ammo de 1.
Si vous ne savez pas ce que DEC DWORD PTR DS:[ESI] signifie, revoyez les bases de l'ASM, encore une fois ce n'est pas le but ici" d'expliquer ce que ça veut dire. :p
[ESI] contient donc notre ammo actuelle en hexadécimal:

cgdBz6r.png


On pourrait rapidement patcher ça en changeant le DEC en INC. Malheureusement le tuto s'arrêterait là. :(
Comme dit précédemment on va hooker à cet endroit pour dire au jeu d'augmenter l'ammo.
3 infos essentielles à chercher dans Olly avant de coder un hook basique:
- Adresse où hooker => dans notre cas 0x45B75F

- Combien de bytes à "écraser":
La fonction que je vais utiliser va placer un JMP à l'adresse où on veut hooker. Hors un JMP prend 5 opcodes (5 bytes)...
Petit rappel de ce qu'on a dans Olly:
MLrOzlP.png

La solution va d'être d'écraser non seulement l'instruction à 0x45B75F mais aussi celle qui suit (c'est-à-dire 0x45B761). Au lieu d'écraser seulement 2 bytes (FF 0E) on va en écraser 6 (FF 0E 8D 74 24 24). C'est plus que la taille de notre JMP et c'est parfait.

- Où revenir:
Après l'exécution de notre hook il faut lui indiquer où revenir dans le code pour que le programme puisse continuer comme si de rien n'était (ou presque :p). On a écrasé 0x45B75F et 0x45B761, on peut donc revenir à l'adresse de l'instruction qui suit: 0x45B765.


Pour le hook j'ai choisi une fonction qui est utilisée par beaucoup de gens (vous pouvez faire la vôtre si besoin):

PHP:
VOID WINAPI DetourFunc(BYTE *pAddress, DWORD dwJumpTo, DWORD dwLen){

	DWORD dwOldProtect, dwBkup, dwRelAddr;
       //MSDN pour VirtualProtect, en gros ça enlève la protection de la page (si jamais elle est protégée)
	VirtualProtect(pAddress, dwLen, PAGE_EXECUTE_READWRITE, &dwOldProtect);

	dwRelAddr = (DWORD)(dwJumpTo - (DWORD)pAddress) - 5;


	*pAddress = 0xE9; // 0xE9 est l'opcode qui veut dire "JMP"

       //  0xE9 + 0x1 => Adresse de notre fonction
	*((DWORD *)(pAddress + 0x1)) = dwRelAddr;

	// C'est ce qui va écraser le nombre de bytes qu'on a spécifié (6)
       //Pourquoi 0x5? un JMP = 0x5, E9 + 0x5 => on arrive juste après l'endroit où on a spécifié l'adresse de la fonction
// Dans notre exemple on veut écraser 6 bytes mais la boucle ne va en réalité écraser qu'un seul opcode, celui qui se trouve juste après notre JMP. Si on avait voulu écraser 7 bytes, la boucle aurait noppé 2 bytes, et ainsi de suite
	for (DWORD x = 0x5; x < dwLen; x++)
		*(pAddress + x) = 0x90;

	VirtualProtect(pAddress, dwLen, dwOldProtect, &dwBkup);

}

Les crédits pour cette fonction vont à -InSaNe-.

On l'utilise comme ceci:
PHP:
DetourFunc(reinterpret_cast<BYTE*>(0x45B75F), reinterpret_cast<uint32_t>(hkIncAmmo), 6);//

On va passer à la fonction en elle-même. Elle va simplement réécrire les instructions écrasées (et changer un peu le dec évidemment :p), puis revenir au code.

PHP:
uint32_t retJump = 0x45B765; //adresse de retour
_declspec(naked) void hkIncAmmo()
{
	__asm{
		inc dword ptr ds:[esi]
		lea esi, dword ptr ss:[esp+0x24] //ne PAS oublier le 0x devant 24, C'est de l'hexadécimal
		push retJump
		ret // push [adresse] + ret => JMP, on retourne donc @0x45B765
	}
}
Rien de plus.

__declspec(naked): une fonction naked n'a pas d'épilogue ni de prologue (push ebp, mov ebp, esp, blablabla), on doit donc nettoyer le stack par nous-mêmes.
Explication très simple: la fonction va placer les instructions à un endroit du code et SEULEMENT ces instructions.

En espérant que ça aura aidé quelqu'un. :)
 
Last edited:
  • Like
Reactions: GADJET76