Jak napisać prosty patch?

Dzisiaj coś dla początkujących, czyli krótki program w C/C++, prezentujący jak używając funkcji WinApi napisać prosty patch, który otworzy wskazany plik i zmodyfikuje kilka bajtów pod konkretnym adresem.

#include <windows.h>

int main()
{
	// bajty, ktore zostana zapisane do pliku
	BYTE cPatch[] = { 0xAA, 0xBB, 0xCC };

	// offset, pod ktorym zostana zapisane powyzsze bajty
	const DWORD dwPatchRawOffset = 0x1234;

	// ilosc zapisanych bajtow w pliku (parametr dla funkcji WriteFile)
	DWORD dwWritten = 0;

	// uchwyt pliku
	HANDLE hFile = INVALID_HANDLE_VALUE;

	// otworz plik wejsciowy w trybie do zapisu (flaga GENERIC_WRITE)
	hFile = CreateFile("plik.exe", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	// jesli nie udalo sie otworzyc pliku - wyswietl komunikat i zakoncz
	if (hFile == INVALID_HANDLE_VALUE)
	{
		MessageBox(NULL, "Nie udalo sie otworzyc pliku wejsciowego!", "Blad", MB_ICONERROR);
		return 1;
	}

	// ustaw wskaznik pliku na offsecie (raw offset),
	// gdzie zostana zapisane zmodyfikowane bajty
	SetFilePointer(hFile, dwPatchRawOffset, 0, FILE_BEGIN);

	// pod wskazanym adresem zapisz bajty z tablicy cPatch
	WriteFile(hFile, cPatch, sizeof(cPatch), &dwWritten, NULL);

	// zamknij plik
	CloseHandle(hFile);

	MessageBox(NULL, "Plik zostal pomyslnie zmodyfikowany", "Informacja", MB_ICONINFORMATION);

	return 0;
}

Źródła w języku angielskim zostały opublikowane na https://github.com/PELock/Simple-Patch-File-Example

PS. Być może ktoś zechce w komentarzach zaprezentować jak taki prosty patch wyglądałby w innych językach programowania. Jestem ciekaw waszych interpretacji oraz usprawnień.

8 komentarzy do “Jak napisać prosty patch?”

  1. Mały code review 😉
    1. Linia #6 przydałby się const.
    2. Linie 15, 18 – spokojnie można zredukować do 1 linii (HANDLE hFile = CreateFile…), zawsze to o jedną inicjalizację mniej. No i jakby bardziej zgodne z RAII
    3. Linie 29, 32 – brak obsługi błędów (plik mniejszy niż 0x1234 = problem)

    Odpowiedz
    • @img: 1. w tym wypadku racja, jednak jak sam wiesz, często bajty patcha ustawiane są dynamicznie, 2. dla C++ to by było prawdą, 3. to tylko prosty patch :), WriteFile() też polegnie na plikach read only, no i po drodze jeszcze kilka rzeczy może się nie udać, jednak nie chciałem tego kodu sprowadzać do absurdu, bo by się skończyło na transakcyjnym api 🙂

      Odpowiedz
  2. Aha, na readonly poległby już CreateFile z open mode GENERIC_WRITE.
    Co do #2 – dlatego napisałem „bardziej”. Jeżeli chcesz low level – to potencjalnie marnujesz parę bajtów na inicjalizację, jako że i tak zaraz nadpisujesz zmienną wynikiem CreateFile. Ale znając życie pierwsza inicjalizacja (linia 15) i tak będzie wycięta przez kompilator jako bezsensowna/nic nie wnosząca.

    Taaak, wiem, czepiam się ;D

    Odpowiedz
  3. No dobra, nie zostało oficjalnie wycofane, ale „Microsoft strongly recommends developers investigate utilizing the discussed alternatives (or in some cases, investigate other alternatives) rather than adopting an API platform which may not be available in future versions of Windows.” http://msdn.microsoft.com/en-us/library/windows/desktop/hh802690(v=vs.85).aspx
    Z kompletnym wycofywaniem nie jest tak kolorowo – np. o GetVersion/GetVersionEx pisali podobnie już jakiś czas temu. W Windows 8.1 funkcje te zaczęły nieco kłamać, a dopiero w Windows 10 SDK zostały oznaczone jako deprecated, co nie znaczy że ich nie ma.
    Jakoś nie dziwię się że TxF nie zdobył wielkiej popularności. Brak porządnej dokumentacji (np dlaczego Windows stwierdził że sobie ustawi stan mojej transakcji na TransactionOutcomeAborted?) zdecydowanie nie przysparzał im fanów 😉

    Co do alternatywnej wersji – uważam że jeżeli chodzi o WinAPI/C to wyczerpałeś temat. A wersję c++14 może popełnię w wolnej chwili 😉

    Odpowiedz
    • @img: się sadzą tylko na te API :), z tym GetVersionEx nie bardzo rozumiem po co łamać kompatybilność z dzisiątkami tysięcy aplikacji, które może nie używają manifestów, interfejs z GetVersionEx nie ma według mnie żadnych wad, dziwi mnie ta polityka, a wręcz denerwuje, dobrze, że nie wycofali tej funkcji z DLL-ek.

      Oficjalne tłumaczenie http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074(v=vs.85).aspx jest po prostu żałosne, wycofali bo programiści nie potrafili poprawnie używać tej funkcji i nie wyrabiali z wypuszczaniem shimów… czytamy dalej – aplikacje niepoprawnie wykrywały OS-a i pluły się, że chcą Windows XP :P, czyli ktoś sprawdzał czy np. major version == 5 i jak nie to się pluł, tragedia, bo świadczy to tylko o błędnym kodzie, ale nie podoba mi się, że głupotę programistów przekłada się na wycofywanie API.

      Co do transakcyjnych API to wyglądało na fajną rzecz, jednak nie widziałem tego wykorzystanego w ani jednej aplikacji, ani nie widziałem chyba wpisu na jakimś blogu czy forum.

      Odpowiedz

Dodaj komentarz