Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: Assembly, C++: Hookkauksen kaatuminen mov-komentoon

Hennkka [12.11.2013 21:47:21]

#

Yritän hookata DirecX9:n Present-funktiota ihan kokeilumielessä. Dll-tiedoston injektoiminen ja funktion muistiosoitteen selvittäminen onnistuvat, mutta hookkaaminen kaatuu hyppykoodin kopioimiseen. Aluksi käytin memcpy:ä, mutta se ei toiminut joten siirryin käsin kopioimiseen debuggaamisen helpottamiseksi. Olen onnistunut paikantamaan bugin assemblykoodiin asti, jossa ohjelma vain pysähtyy kohtaan, jossa hyppykoodi kopioitaisiin kohteeseensa. Suoritus ei vain ikinä palaa kyseiseltä riviltä (merkitty assemblylistaukseen). Ohessa on hookkaamiseen käyttämäni koodi sekä Redirect-funktiosta kohta deassembloituna.

#pragma once

#include <Windows.h>

#include <string>
#include <sstream>
using namespace std;

class Hook
{
private:
	BYTE jumpCode[5];
	BYTE origCode[5];
	volatile LPVOID origFunc;
	DWORD oldProtection;
public:

	Hook() {
		jumpCode[0] = 0;
	}

	Hook(LPVOID originalFunction, LPVOID newFunction) : origFunc(originalFunction)
	{
		jumpCode[0] = 0;
		origFunc = originalFunction;

		DWORD newProtection = PAGE_EXECUTE_READWRITE;
		if (!VirtualProtect(origFunc, 8, newProtection, &oldProtection))
			throw "Failed to change function protection level";

		// Compute new jump code
		jumpCode[0] = 0xE9;		// Long jump
		DWORD offset = (DWORD)((UINT_PTR)newFunction - (UINT_PTR)origFunc) - sizeof(jumpCode);
		memcpy(jumpCode+1, &offset, sizeof(offset));

		stringstream ss;
		ss << hex;
		for (int i = 0; i < 8; i++)
			ss << (unsigned int)(((char*)originalFunction)[i]) << " ";
		MessageBoxA(0, ss.str().c_str(), "Code before replace", 0);

		// Copy original code to memory
		memcpy(origCode, origFunc, sizeof(origCode));
		// And replace it with jump code
		//memcpy(origFunc, jumpCode, sizeof(jumpCode));
	}

	~Hook(void)
	{
		// Remove created hook
		if (origFunc == 0 || jumpCode[0] == 0)
			return;	// Not even hooked so no hook to be removed

		memcpy(origFunc, origCode, sizeof(origCode));
		DWORD tmpProtection;
		VirtualProtect(origFunc, 8, oldProtection, &tmpProtection);
		jumpCode[0] = 0;
	}

	void Original() {
		stringstream ss;
		ss << hex << "Original: " << (int) origFunc;
		MessageBoxA(0, ss.str().c_str(), "", 0);
		memcpy(origFunc, origCode, sizeof(origCode));
	}

	void Redirect() {
		stringstream ss;
		ss << hex << "Redirect: " << (int) origFunc;
		MessageBoxA(0, ss.str().c_str(), "", 0);
		char *ptr = (char*) ((int)origFunc);
		ptr[0] = jumpCode[0];
		ptr[1] = jumpCode[1];
		ptr[2] = jumpCode[2];
		ptr[3] = jumpCode[3];
		ptr[4] = jumpCode[4];
		//memcpy(origFunc, jumpCode, sizeof(jumpCode));
	}
};
		char *ptr = (char*) ((int)origFunc);
53656832 8B 45 E8             mov         eax,dword ptr [this]
53656835 8B 48 0C             mov         ecx,dword ptr [eax+0Ch]
53656838 89 8D 24 FF FF FF    mov         dword ptr [ptr],ecx
		ptr[0] = jumpCode[0];
5365683E B8 01 00 00 00       mov         eax,1
53656843 6B C0 00             imul        eax,eax,0
53656846 B9 01 00 00 00       mov         ecx,1
5365684B 6B C9 00             imul        ecx,ecx,0
5365684E 8B 95 24 FF FF FF    mov         edx,dword ptr [ptr]
53656854 8B 75 E8             mov         esi,dword ptr [this]
53656857 8A 04 06             mov         al,byte ptr [esi+eax]
5365685A 88 04 0A             mov         byte ptr [edx+ecx],al  <-- Pysähtyy tälle riville
		ptr[1] = jumpCode[1];
5365685D B8 01 00 00 00       mov         eax,1
53656862 C1 E0 00             shl         eax,0
53656865 B9 01 00 00 00       mov         ecx,1
5365686A C1 E1 00             shl         ecx,0
5365686D 8B 95 24 FF FF FF    mov         edx,dword ptr [ptr]
53656873 8B 75 E8             mov         esi,dword ptr [this]
53656876 8A 04 06             mov         al,byte ptr [esi+eax]
53656879 88 04 0A             mov         byte ptr [edx+ecx],al
		ptr[2] = jumpCode[2];
5365687C B8 01 00 00 00       mov         eax,1
53656881 D1 E0                shl         eax,1
53656883 B9 01 00 00 00       mov         ecx,1
53656888 D1 E1                shl         ecx,1
5365688A 8B 95 24 FF FF FF    mov         edx,dword ptr [ptr]
53656890 8B 75 E8             mov         esi,dword ptr [this]
53656893 8A 04 06             mov         al,byte ptr [esi+eax]
53656896 88 04 0A             mov         byte ptr [edx+ecx],al
		ptr[3] = jumpCode[3];
53656899 B8 01 00 00 00       mov         eax,1
5365689E 6B C0 03             imul        eax,eax,3
536568A1 B9 01 00 00 00       mov         ecx,1
536568A6 6B C9 03             imul        ecx,ecx,3
536568A9 8B 95 24 FF FF FF    mov         edx,dword ptr [ptr]
536568AF 8B 75 E8             mov         esi,dword ptr [this]
536568B2 8A 04 06             mov         al,byte ptr [esi+eax]
536568B5 88 04 0A             mov         byte ptr [edx+ecx],al
		ptr[4] = jumpCode[4];
536568B8 B8 01 00 00 00       mov         eax,1
536568BD C1 E0 02             shl         eax,2
536568C0 B9 01 00 00 00       mov         ecx,1
536568C5 C1 E1 02             shl         ecx,2
536568C8 8B 95 24 FF FF FF    mov         edx,dword ptr [ptr]
536568CE 8B 75 E8             mov         esi,dword ptr [this]
536568D1 8A 04 06             mov         al,byte ptr [esi+eax]
536568D4 88 04 0A             mov         byte ptr [edx+ecx],al

Käytän Visual Studio 2012 Expressiä koodin kirjoittamiseen sekä debuggaamiseen.

Karethoth [12.11.2013 22:32:21]

#

Pikaisella läpiluvulla ongelma on siinä että yrität kirjoittaa kirjoitussuojattuun muistiin, olettaen siis että osoitin todellakin osoittaa alkuperäiseen funktioon. Ja tätähän käsittääkseni loppujen lopuksi yritätkin tehdä.
Yksinkertaistaen syy on kirjoitussuojaus virtuaalimuistin alueilla, jotka on omistettu ohjelmakoodiksi merkatulle datalle.

Edit: Pari tutustumisen arvoista linkkiä, jotka ainakin kokeiluissa auttavat:
VirtualProtect
WriteProcessMemory

Hennkka [13.11.2013 22:01:48]

#

Juu, oikeuksissahan se vika olikin, vaikka muutinkin muistialueen oikeudet oikeiksi, niin destruktoria kutsuttiin ja se palautti kirjoitussuojauksen takaisin käyttöön. Muutin koodia nyt siten, että en enää luo uutta Hook-instanssia vaan kutsun vanhan Init-funktiota, joka on oleellisesti sama kuin nykyinen konstruktori.

Ps. Vika johtui siis siitä, että uuden instanssin destruktoria kutsuttiin tällä rivillä:

presentHook = Hook(original_DX9_Present, DX9_Present);

Joku C++:aa syvemmin osaava voi selittää, miksi sitä kutsutaan. Ymmärtääkseni johtuu siitä, että uutta arvoa asettaessa uusi instanssi kopioidaan vanhan päälle ja sitten alkuperäinen tuhotaan.

Metabolix [13.11.2013 22:37:54]

#

Tuolla rivillä luodaan väliaikainen Hook-instanssi, kutsutaan sijoitusoperaattoria ja lopuksi tuhotaan väliaikainen instanssi. Siis suunnilleen näin:

{
	Hook tmp(foo, bar);
	presentHook = tmp;
	// Lohko loppuu, tmp tuhoutuu; tmp.~Hook()
}

Tämä ei mielestäni ole kovin syvällistä tietoa; jokaisen C++-ohjelmoijan pitäisi ymmärtää, että oliomuuttujat eivät ole viittauksia ja siis sijoitusoperaattori ei vaihda oliota toiseksi vaan kopioi arvoja.

Jos ylipäänsä kirjoitat tuollaista koodia, sinun pitäisi ehkä käyttää osoittimia:

Hook* presentHook = new Hook(foo, bar);

delete presentHook;
presentHook = new Hook(foo2, bar2);

Tai nykyaikaisemmin osoitinluokkia:

std::shared_ptr<Hook> presentHook(new Hook(foo, bar));

presentHook.reset();
presentHook.reset(new Hook(foo2, bar2));

Jos haluaa kirjoittaa harvinaista koodia ja brassailla taidoillaan, voi kutsua tuhoajaa ja muodostinta myös ilman osoittimia:

Hook presentHook(foo, bar);

presentHook.~Hook();
new (&presentHook) Hook(foo2, bar2);

Tietenkin myös erillisen muutosfunktion käyttö on mahdollista, jos se sopii kuvioihin. C++11 lisää valikoimaan siirtomuodostimet.

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta