DLL Injection - ERROR_BAD_EXE_FORMAT

0

Witam,
problem, który mnie dotknął jest tak absurdalny, że wątpię, że ktokolwiek tutaj jest w stanie go rozwiązać.
Zatem, tworzę pewien mod do gry, który wykorzystuje DLL injection. Wszystko jest oparte o 32 bitowe binarki (gra, dll, launcher). Problemem jest to, że dla mnie i dla kilkudziesięciu tysięcy innych ludzi z różnymi komputerami wszystko pięknie działa, jednak dzisiaj pojawił się gość z drugiego końca świata, któremu nie działa. Problem jest w miejscu ładowania mojej dllki. Konkretniej, LoadLibrary z parametrem ścieżki do mojej dllki zwraca NULL, a GetLastError zwraca kod błędu 193, który oznacza ERROR_BAD_EXE_FORMAT. W ogóle żeby odczytać ten kod błędu musiałem się nieźle nagimnastykować, ale o tym za chwilę. Ogólnie miałem dostęp do komputera tego człowieka przez teamviewer, sprawdziłem wszystko co właściwie mogłoby powodować błędy (binarka gry, dll, launcher, ponowne pobranie binarek, itd.), i nic właściwie nie znalazłem. OS to Windows 10 64bit.

Teraz trochę kodu:

 
struct SInjectionInfo
{
	unsigned pfnMessageBoxA;
	unsigned pfnLoadLibraryA;
	unsigned pfnGetLastError;
	char filename[260];
	char msg[1024];
};

typedef int(__stdcall *MESSAGEBOXA)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);
typedef HMODULE(__stdcall *LOADLIBRARYA)(LPCTSTR lpFileName);
typedef DWORD(__stdcall *GETLASTERROR)(void);

DWORD __stdcall Inject(LPVOID lpThreadParameter)
{
	SInjectionInfo *info = (SInjectionInfo *)lpThreadParameter;

	MESSAGEBOXA pfnMessageBox = (MESSAGEBOXA)info->pfnMessageBoxA;
	LOADLIBRARYA pfnLoadLibrary = (LOADLIBRARYA)info->pfnLoadLibraryA;
	GETLASTERROR pfnGetLastError = (GETLASTERROR)info->pfnGetLastError;

	pfnMessageBox(0, info->filename, info->msg, 0);

	HANDLE hdl = pfnLoadLibrary(info->filename);
	int n = pfnGetLastError();

	if (hdl)
		n = 0;

	{ // itoa implementation
		int i, sign, j;
		char c;

		if ((sign = n) < 0)  /* record sign */
			n = -n;          /* make n positive */
		i = 0;
		do {       /* generate digits in reverse order */
			info->msg[i++] = n % 10 + 48;   /* get next digit */
		} while ((n /= 10) > 0);     /* delete it */
		if (sign < 0)
			info->msg[i++] = 45;
		info->msg[i] = 0;

		int len = i;

		for (i = 0, j = len - 1; i<j; i++, j--) {
			c = info->msg[i];
			info->msg[i] = info->msg[j];
			info->msg[j] = c;
		}
	}

	pfnMessageBox(0, info->msg, 0, 0); // tu u mnie wyświetla 0, a u brazylijczyka 193 (zawsze)

	return 1;
}

const char *InjectDll(HANDLE hProcess, const char* szDllPath)
{
	const char *result = "unknown result";

	SIZE_T bytesWritten = 0;

	const int iCodeSize = 2000;
	void* pRmtCode = VirtualAllocEx(hProcess, NULL, iCodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	WriteProcessMemory(hProcess, pRmtCode, (void *)Inject, iCodeSize, &bytesWritten);

	SInjectionInfo info;
	strcpy(info.filename, szDllPath);
	strcpy(info.msg, "Injector");
	info.pfnMessageBoxA = (unsigned)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
	info.pfnLoadLibraryA = (unsigned)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
	info.pfnGetLastError = (unsigned)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetLastError");

	void* pRmtInfo = VirtualAllocEx(hProcess, NULL, sizeof(SInjectionInfo), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	WriteProcessMemory(hProcess, pRmtInfo, (void *)&info, sizeof(SInjectionInfo), &bytesWritten);

	FlushInstructionCache(hProcess, 0, 0);

	DWORD threadId = 0;
	HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pRmtCode, pRmtInfo, 0, &threadId);
	if (hThread && hThread != INVALID_HANDLE_VALUE)
	{
		WaitForSingleObject(hThread, INFINITE);

		DWORD dwExitCode = 0;
		GetExitCodeThread(hThread, &dwExitCode);
		if (dwExitCode != 0)
		{
			result = 0;
		}
		else
		{
			result = "initialization of client failed";
		}

		CloseHandle(hThread);
	}
	else
	{
		result = "can not create remote thread";
	}
				
	return result;
}

Jak widać, specjalnie napisałem funkcję Inject aby tylko poznać kod błędu. Ścieżka do dllki została sprawdzona, wszystkie pointery, i inne, i wszystko się zgadza. To wygląda tak, jakby jego Windows odgórnie wiedział, że mój plik DLL jest 64 bitowy, i nie chciał go załadować. Lub ewentualnie Windows w tym miejscu:

GetModuleHandleA("kernel32.dll")

ładuje kernel32.dll 64 bitowe, w 32 bitowym launcherze...

0

Co prawda napisałeś, że ścieżka do dllki została sprawdzona ale czy na pewno ładujesz 32 bitowy kernel32.dll, a wersję 64 bitową?
C:\Windows\System32\kernel32.dll na 64 bitowym kompie jest 64 bitowe, C:\Windows\SysWow64\kenel32.dll jest 32 bitowe.

0

@Satirev, popatrz, że ja ładuję kernel32.dll w taki sposób:

GetModuleHandleA("kernel32.dll")

I to jest robione w 32 bitowym launcherze. A możemy tak zrobić bo wiemy, że kernel32.dll jest załadowane już, i możemy jego handle pobrać. Tak wiem o tym, że na 64 bitowym systemie:
C:\Windows\System32\kernel32.dll - 64 bit
C:\Windows\SysWow64\kenel32.dll - 32 bit
Jednak nie ma jak tego właściwie sprawdzić po pobraniu. GetModuleFileName zwróci zawsze C:\Windows\System32\kernel32.dll, i nie ważne czy to jest 32 bit czy 64 bit.

0

Nikt nie ma żadnych pomysłów? Dzisiaj kolejny człowiek przyszedł, który ma problem dokładnie taki sam. Nie wiem co ja mam im powiedzieć...

Link do dllki: http://www58.zippyshare.com/v/F9fuhbAM/file.html
Może z nią coś jest źle, coś jest źle ustawione w kompilatorze.

0

32-bitowy proces na 64-bitowym systemie działa w pewnej warstwie emulacji. To co 32-bitowy proces widzi jako c:\windows\system32 jest w rzeczywistości przekierowywane do c:\windows\syswow64.

W 32-bitowym kodzie coś takiego GetModuleHandleA("kernel32.dll") da ci handlę do 32-bitowej biblioteki. Nie ma możliwości załadowania 64-bitowej biblioteki w 32-bitowym programie ani na odwrót.

Zastanawia mnie po co w ogóle te GetProcAddress robisz, skoro wszystkie te funkcje są normalnie dostępne pod <windows.h>.

0

Źle interpretujecie funkcję DWORD __stdcall Inject(LPVOID lpThreadParameter). Tak na prawdę ona powinna być napisana w assemblerze, ale że mi się nie chciało, to postanowiłem, że napiszę to w C i wyjdzie na to samo. Ta funkcja nie może się odwoływać do czegokolwiek poza nią, stąd robię te GetProcAddress, bo adresy do funkcji z np. kernel32.dll są we wszystkich procesach takie same. Tak jak już napisałem, funkcja ta miała określić gdzie leży problem, i spisała się dobrze. Normalny launcher wygląda tak: (i nie ma możliwości wyciągnięcia z niego informacji o źle załadowanej dllce):

 bool CLauncher::CreateGameProcess(const wchar_t *szCmdLine)
{
	this->SetEnvironmentVariables();

	// Create game process
	STARTUPINFOW siStartupInfo = { 0 };
	PROCESS_INFORMATION piProcessInformation = { 0 };
	siStartupInfo.cb = sizeof(siStartupInfo);

	wchar_t szParams[1024] = { 0 }; // enough?
	swprintf(szParams, L"\"%s\" %s", m_szGameExeDirectory, szCmdLine);
	if (!CreateProcessW(m_szGameExeDirectory, szParams, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, m_szGameDirectory, &siStartupInfo, &piProcessInformation))
	{
		MessageBox(NULL, "Cannot create game process.", "ETS2MP - Error", MB_ICONERROR);
		return false;
	}

	// Inject dll
	wchar_t szPath[MAX_PATH] = { 0 };
	wcscpy(szPath, GetClientPath());

#ifdef DEBUG
	wcscat(szPath, L"\\core_d.dll");
#else
	wcscat(szPath, L"\\core.dll");
#endif

	const wchar_t *injectResult = InjectDll(piProcessInformation.hProcess, szPath);
	if (injectResult != 0)
	{
		wchar_t szMessageError[1024] = { 0 };
		swprintf(szMessageError, L"Could not inject dll into the game process (%s). Please try launching the game again. If this problem still occurs, please try reinstalling the mod.", injectResult);
		MessageBoxW(NULL, szMessageError, 0, MB_ICONERROR);
		TerminateProcess(piProcessInformation.hProcess, 0);
		return false;
	}

	// Restore process thread
	ResumeThread(piProcessInformation.hThread);

	return true;
}

const wchar_t *CLauncher::InjectDll(HANDLE hProcess, const wchar_t *szDllPath)
{
	const wchar_t *result = L"unknown result";

	SIZE_T sLibPathLen = (wcslen(szDllPath) + 1) * sizeof(wchar_t);
	void *pRmtLibraryPath = VirtualAllocEx(hProcess, NULL, sLibPathLen, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	if (!pRmtLibraryPath)
	{
		return L"can not allocate memory";
	}

	SIZE_T bytesWritten = 0;
	BOOL bWriteProcessMem = WriteProcessMemory(hProcess, pRmtLibraryPath, (void *)szDllPath, sLibPathLen, &bytesWritten);
	if (bytesWritten != sLibPathLen)
	{
		return L"bytes written and path length does not match";
	}

	if (bWriteProcessMem)
	{
		HMODULE hKernel32 = GetModuleHandleW(L"Kernel32");
		if (hKernel32)
		{
			FARPROC pfnLoadLibraryW = GetProcAddress(hKernel32, "LoadLibraryW");
			if (pfnLoadLibraryW)
			{
				DWORD threadId = 0;
				HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pfnLoadLibraryW, pRmtLibraryPath, 0, &threadId);
				if (hThread)
				{
					WaitForSingleObject(hThread, INFINITE);

					DWORD dwExitCode = 0;
					GetExitCodeThread(hThread, &dwExitCode);
					if (dwExitCode != 0)
					{
						result = NULL;
					}
					else
					{
						result = L"initialization of client failed";
					}

					CloseHandle(hThread);
				}
				else
				{
					result = L"can not create remote thread";
				}
			}
			else
			{
				result = L"can not get LoadLibraryA address";
			}
			FreeModule(hKernel32);
		}
		else
		{
			result = L"can not get module handle kernel32.dll";
		}
		VirtualFreeEx(hProcess, pRmtLibraryPath, sizeof(pRmtLibraryPath), MEM_RELEASE);
	}
	else
	{
		result = L"can not write process memory";
	}
	return result;
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1