#cpp #winapi #pe
Есть один известный крэкми. После его изучения оказывается, что для его взлома достаточно
пропатчить два байта по адресу 40138B (забить условный переход NOP'ами) и два байта
по адресу 401243 (прописать там безусловный переход). Я хочу написать программу, которая
делает это автоматически.
#include
#include
#include
#include
enum {OK, CANT_OPEN};
int patch (TCHAR* fname)
{
char first_patch[2] = {0x90, 0x90}; // NOP NOP
char second_patch[2] = {0xEB, 0x07}; // JMP 40124C
HANDLE hFile = CreateFile(fname,
FILE_ALL_ACCESS, NULL,
NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return CANT_OPEN;
int SizeFile = GetFileSize(hFile, NULL);
HANDLE MhFile = CreateFileMapping(hFile, 0, PAGE_READWRITE, 0, SizeFile, 0);
HANDLE View = MapViewOfFile(MhFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
UnmapViewOfFile(View);
CloseHandle(hFile);
CloseHandle(MhFile);
return OK;
}
int main (int argc, char *argv[])
{
TCHAR* default_fname = "CRACKME.EXE";
if (argc > 1)
{
default_fname = argv[1];
}
patch(default_fname);
std::cout << default_fname << std::endl;
return 0;
}
Я спроецировал исполняемый файл в адресное пространство процесса и получил адрес
этой проекции. Теперь надо пропатчить этот образ, после чего процедура UnmapViewOfFile
сохранит изменения на диск.
Как в этой проекции найти адреса типа 40138B и 401243? Где можно посмотреть теорию
и примеры по редактированию PE-файлов?
Далее пишу хрень, можно не читать.
Успешно напечатал сигнатуру MZ, это значит, что при помощи View можно заполнять структуры
заголовков, используя, возможно, для выравнивания макросы Криса Касперски.
char M = *((char *) View);
char Z = *((char *) View + 1);
std::cout << M << Z << std::endl;
Проверка того, что заголовок дос успешно загружен. Потом добавлю код для отлавливания
возможных ошибок.
unsigned myRVAtoRAW(HANDLE pe_image, unsigned rva)
{
IMAGE_DOS_HEADER dos_header;
std::memcpy(reinterpret_cast(&dos_header), pe_image, sizeof(dos_header));
std::cout << (dos_header.e_magic == 'ZM') << std::endl;
return 0;
}
Ответы
Ответ 1
Можно пойти двумя путями:
Разобраться и написать самому. Тогда рекомендую почитать статью: Разработка функций
RvaToRaw и RawToRva.
Либо воспользоваться функций из известной dbghelp.dll.
Я бы посоветовал разобраться, благо это не сложно. Также следует отметить знаменитую
статью Об упаковщиках в последний раз: Часть первая - теоретическая
Ответ 2
Что касаемо патчинга:
int main() {
HWND hWnd = FindWindow(0, "Calculator");
if(hWnd == 0){
MessageBox(0, "Error cannot find window.", "Error", MB_OK|MB_ICONERROR);
} else {
DWORD proccess_ID;
GetWindowThreadProcessId(hWnd, &proccess_ID);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, proccess_ID);
if(!hProcess){
MessageBox(0, "Could not open the process!", "Error!", MB_OK|MB_ICONERROR);
} else {
DWORD newdatasize = sizeof(newdata);
if(WriteProcessMemory(hProcess, (LPVOID)0x40138B , "0x90", newdatasize, NULL)){
MessageBox(NULL, "WriteProcessMemory worked.", "Success", MB_OK + MB_ICONINFORMATION);
} else {
MessageBox(NULL, "Error cannot WriteProcessMemory!", "Error", MB_OK + MB_ICONERROR);
}
CloseHandle(hProcess);
}
}
return 0;
}
Адреса искать по паттерну(уникальной сигнатуре), сделать ее можно в любом отладчике