Nawiązując do artykułu Jak napisać prosty patch? i komentarzy @ img (hi mate), prezentuję bardziej rozbudowaną wersję patchera z całą gamą sprawdzania potencjalnych błędów. Jeśli zauważycie więcej potencjalnych kłopotów – piszcie w komentarzach (sam jestem ciekaw, co jeszcze może pójść nie tak w jak się wydaje prostej operacji).
#define UNICODE
#include <windows.h>
#include <Sfc.h>
// zalacz biblioteke SFC.lib (mechanizm ochrony plikow systemowych WFP / WRP)
#pragma comment(lib, "sfc")
int main()
{
// pelna sciezka pliku do spatchowania
const wchar_t * wszFilePath = L"C:\\plik.exe";
// bajty, ktore zostana zapisane do pliku
const BYTE cPatch[] = { 0xAA, 0xBB, 0xCC };
// offset, pod ktorym zostana zapisane powyzsze bajty
const DWORD dwPatchRawOffset = 0x1234;
// rozmiar pliku
DWORD dwFileSize = 0;
// ilosc zapisanych bajtow w pliku (parametr dla funkcji WriteFile)
DWORD dwWritten = 0;
// kod bledu z funkcji zapisu do pliku WriteFile
BOOL bWriteFileResult = FALSE;
// sprawdz czy plik ma atrybuty read only lub znajduje sie na nosniku jak np. DVD
DWORD dwFileAttributes = GetFileAttributes(wszFilePath);
// czy udalo sie odczytac atrybuty?
if (dwFileAttributes == INVALID_FILE_ATTRIBUTES)
{
MessageBox(NULL, L"Nie mozna odczytac atrybutow pliku (plik nie istnieje?)!", L"Blad", MB_ICONERROR);
return 1;
}
// czy plik ma atrybuty tylko do odczytu?
if ( (dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0)
{
MessageBox(NULL, L"Wybrany plik nie ma praw do zapisu (lub znajduje sie na takim nosniku jak np. DVD)!", L"Blad", MB_ICONERROR);
return 2;
}
// czy plik jest chronionym plikiem systemowym?
if (SfcIsFileProtected(NULL, wszFilePath) == TRUE)
{
MessageBox(NULL, L"Wybrany plik jest chronionym plikiem systemowym i automatycznie zostanie odtworzony po zmianie przez system Windows!", L"Blad", MB_ICONERROR);
return 3;
}
// otworz plik wejsciowy w trybie do zapisu (flaga GENERIC_WRITE)
HANDLE hFile = CreateFile(wszFilePath, 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, L"Nie udalo sie otworzyc pliku wejsciowego!", L"Blad", MB_ICONERROR);
return 4;
}
// pobierz rozmiar pliku
dwFileSize = GetFileSize(hFile, NULL);
// czy plik jest pusty?
if (dwFileSize == 0)
{
CloseHandle(hFile);
MessageBox(NULL, L"Plik jest pusty (0 bajtow)!", L"Blad", MB_ICONERROR);
return 5;
}
// czy wskaznik pliku, gdzie ma byc spatchowany przekracza rozmiar pliku?
if ( (dwPatchRawOffset + sizeof(cPatch)) > dwFileSize)
{
CloseHandle(hFile);
MessageBox(NULL, L"Wskaznik do spatchowania znajduje sie poza rozmiarem pliku!", L"Blad", MB_ICONERROR);
return 6;
}
// ustaw wskaznik pliku na offsecie (raw offset),
// gdzie zostana zapisane zmodyfikowane bajty
if (SetFilePointer(hFile, dwPatchRawOffset, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
{
CloseHandle(hFile);
MessageBox(NULL, L"Nie udalo sie ustawic wskaznika do spatchowania w wybranym pliku!", L"Blad", MB_ICONERROR);
return 7;
}
// pod wskazanym adresem zapisz bajty z tablicy cPatch
bWriteFileResult = WriteFile(hFile, cPatch, sizeof(cPatch), &dwWritten, NULL);
// czy udalo sie zapisac do pliku?
if (bWriteFileResult == FALSE)
{
CloseHandle(hFile);
MessageBox(NULL, L"Wystapil blad zapisu do pliku!", L"Blad", MB_ICONERROR);
return 8;
}
// czy zapisane zostaly wszystkie bajty patcha?
if (dwWritten != sizeof(cPatch))
{
CloseHandle(hFile);
MessageBox(NULL, L"Nie udalo sie zapisac wszystkich bajtow!", L"Blad", MB_ICONERROR);
return 9;
}
// zapisz wyniki na dysk
if (FlushFileBuffers(hFile) == FALSE)
{
CloseHandle(hFile);
MessageBox(NULL, L"Nie udalo sie zapisac zmian na dysk!", L"Blad", MB_ICONERROR);
return 10;
}
// zamknij plik
CloseHandle(hFile);
MessageBox(NULL, L"Plik zostal pomyslnie zmodyfikowany", L"Informacja", MB_ICONINFORMATION);
return 0;
}
Źródła w języku angielskim zostały także opublikowane na https://github.com/PELock/Patch-File-Example