Салют, Хабр! В предыдущей статье "Патчим Chrome для работы с YouTube" заменой всего 8 байт в файле chrome.dll мы добились работоспособности YouTube, правда работает это не у всех, но у многих. Теперь напишем программу, которая ищет непропатченный процесс Chrome в ОЗУ компьютера и меняет его прямо "на лету". Права администратора при этом не требуются.

За основу возьмём проект https://github.com/glmcdona/Process-Dump/releases/tag/v2.1.1 который умеет дампить любой процесс на диск. Для этого запустим Chrome и сдампим на диск с использованием утилиты pd64.exe из этого проекта.

pd64.exe -p chrome.exe

Получим список PID

PID Process Name
0xe44 chrome.exe
0x1734 chrome.exe
0x1c0c chrome.exe
0x1da0 chrome.exe
0x1b18 chrome.exe
0x20c4 chrome.exe
0xf64 chrome.exe
0x1ac8 chrome.exe
0x9b4 chrome.exe
0xb48 chrome.exe
0xc1c chrome.exe
0xf58 chrome.exe
0x1230 chrome.exe
0xca8 chrome.exe
0x1854 chrome.exe
0x75c chrome.exe
0x223c chrome.exe
0x168c chrome.exe

Как их много! Далее сдампим любой из них, например, первый:

pd64.exe -pid 0xe44

Через непродолжительное время получим кучу файлов в текущей директории и самый большой из них имеет примерно такое имя:

chrome_exe_PIDe44_chrome.dll_7FFB11AB0000_x64.dll

Это и есть искомый chrome.dll который лежал себе в памяти, а теперь был сдамплен на диск.

Рассмотрим процедуру дампа в исходниках проекта Process-Dump и заменим её на процедуру патчинга. Заменяем начало функции bool pe_header::write_image( char* filename ) на следующее:

char mypatch[18]={0x48,0x83,0xB9,0x98,0x00,0x00,0x00,0x00,0x0F,0x85,0xB9,0x00,0x00,0x00,0x48,0x8B,0x47,0x30};
char newbyte[8]={0x66,0xC7,0x47,0x10,0xE8,0x03,0xEB,0x1B};

bool pe_header::write_image( char* filename )
{
	char output[20];
	SIZE_T out_read=0;
	SIZE_T out_write=0;
	int i;

	if (strstr(filename, "_chrome.dll_")==NULL) return false;

	for(i=0;i<_disk_image_size; i++)
	{
		if ((unsigned char)_disk_image[i]==(unsigned char)mypatch[0] && 
			(unsigned char)_disk_image[i+1]==(unsigned char)mypatch[1] && 
			memcmp(_disk_image+i+2,mypatch+2,sizeof(mypatch)-2)==0)
		{
			if (WriteProcessMemory(glob_ph, (LPVOID)(glob_base+i), newbyte, sizeof(newbyte), &out_write) && out_write == sizeof(newbyte))
			{
				printf("patch OK\n");
				return true;
			}
			break;
		}
	}

	printf("patch NOT FOUND\n");
	return true;

	// Writes the loaded and reconstructed memory image to a file

Небольшой комментарий - сначала проверяем, что приходящий на вход функции файл имеет внутри себя подстроку chrome.dll (например, такую - chrome_exe_PIDe44_chrome.dll_7FFB11AB0000_x64.dll) и если это так, то ищем патч-строку mypatch внутри байтового массива disk_image и заменяем её в памяти с помощью функции WriteProcessMemory на строку newbyte. Эти строки mypatch и newbyte мы нашли в предыдущей статье v1.0

Далее нужно "протянуть" переменные glob_ph и glob_base из вызывающей функции, так как в классе pe_header их нет, а для WriteProcessMemory они нужны.

Файлы pe_header.h и dump_process.cpp

public:
	HANDLE glob_ph;
	char *glob_base;
header->glob_ph = _ph;
header->glob_base = (char*)base;

Далее напишем вместо старого tmain небольшой "запускающий код" который будет вызывать старый tmain в мультипотоке, так как chrome создает много разных процессов, надо будет запатчить их все, для повышения скорости патчинга будут задействованы все ядра CPU.

void runpatch(void *data)
{
	DWORD pid=(DWORD)data;
	_TCHAR* myargv[3];
	_TCHAR arg1[100];
	_TCHAR arg2[100];

	myargv[0]=L"pd.exe"; //любая строка
	myargv[1]=arg1;
	myargv[2]=arg2;

	wsprintf(arg1, L"-pid");
	wsprintf(arg2, L"0x%x", pid);
	_tmain222(3, myargv);
}

//будем находить pid процесса chrome.exe (0x12345) 
//и вызывать старый _tmain222 с параметрами -pid 0x12345
int _tmain(int argc, _TCHAR* argv[])
{
	char* processNameFilter = new char[100 + 1];
	sprintf( processNameFilter, "%S", L"chrome.exe" );
	
	// First gather the process matches
	DynArray<process_description*> matches;
	int count = process_find( processNameFilter, &matches );

	if( count > 1 )
	{
		// Create our threads that process the dumping of processes as they close
		thread** threads = new thread*[count];
		for (int i = 0; i < count; i++)
		{
			threads[i] = new thread(&runpatch, (void*)(matches[i]->pid));
		}
		// Wait for all worker threads to complete
		for (int i = 0; i < count; i++)
		{
			threads[i]->join(); // blocks until each thread is finished
			delete threads[i];
			threads[i] = NULL;
		}
		delete[]threads;
		printf("!!!!!!!!! Patching done\n");
	}
	else
	{
		printf("Please run chrome.exe to use YouTube patch\n");
	}
}

Функция process_find была взята из проекта ProcessDump (опция -p), она достаёт из системы все pid процесса chrome.exe и заносит в массив matches, далее вытаскиваем их через matches[i]->pid и в мультипотоке скармливаем функции runpatch которая вызывает старый tmain, переименованный в tmain222, передавая ему параметры -pid XXX для старта дампинга. А дампинг нами уже был заменён на патчинг - что и приводит к желаемому результату!

А именно - после окончания патчинга в Chrome начинает открываться сайт YouTube (и перестаёт открываться login.microsoft.com) - почему это происходит, потому что патчинг меняет TLS протокол, деля tls-пакеты на мелкие кусочки в handshake протоколе, после этого Microsoft не умеет собирать их обратно, а YouTube умеет.

Запускаем Chrome (проверено на всех последних версиях, текущая 141.0.7390.108) и запускаем скомпилированный нами pd.exe - получаем следующую картинку

Запускам pd.exe
Запускам pd.exe

Каждый зеленый patch OK соответствует одному пропатченному процессу Chrome. Запускаем YouTube из работающего Chrome, сам Chrome при этом перезапускать не надо - он пропатчен прямо в ОЗУ. Работает!

В заключение выкладываю на гугл-диск исходники и exe-файл этого проекта -
файл patch_youtube_v2.0.rar пароль 123