Skip to content

Instantly share code, notes, and snippets.

@blastrock
Last active September 5, 2022 09:50
Show Gist options
  • Save blastrock/6958033f03a0bdffa52c6dfa2ce0e60a to your computer and use it in GitHub Desktop.
Save blastrock/6958033f03a0bdffa52c6dfa2ce0e60a to your computer and use it in GitHub Desktop.
dll injector
#include <windows.h>
#include <tlhelp32.h>
#include <iostream>
#include <string>
#define LOG_LINE(x, msg) std::cout << msg << std::endl;
DWORD GetProcessID64(std::wstring processName)
{
PROCESSENTRY32 processInfo;
processInfo.dwSize = sizeof(processInfo);
HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (processesSnapshot == INVALID_HANDLE_VALUE)
return 0;
Process32First(processesSnapshot, &processInfo);
if (_wcsicmp(processName.c_str(), processInfo.szExeFile) == 0)
{
BOOL iswow64 = FALSE;
// https://stackoverflow.com/questions/14184137/how-can-i-determine-whether-a-process-is-32-or-64-bit
// If IsWow64Process() reports true, the process is 32-bit running on a
// 64-bit OS So we want it to return false (32 bit on 32 bit os, or 64 bit on
// 64 bit OS, since we build x64 the first condition will never satisfy since
// they can't run this exe)
auto hProcess =
OpenProcess(PROCESS_ALL_ACCESS, FALSE, processInfo.th32ProcessID);
if (hProcess == NULL)
{
LOG_LINE(INFO, "Error on OpenProcess to check bitness");
}
else
{
if (IsWow64Process(hProcess, &iswow64))
{
// LOG_LINE(INFO, "Rocket league process ID is " <<
// processInfo.th32ProcessID << " | " << " has the WOW factor: " <<
// iswow64);
if (!iswow64)
{
CloseHandle(processesSnapshot);
return processInfo.th32ProcessID;
}
}
else
{
LOG_LINE(INFO, "IsWow64Process failed bruv " << GetLastError());
}
CloseHandle(hProcess);
}
}
while (Process32Next(processesSnapshot, &processInfo))
{
if (_wcsicmp(processName.c_str(), processInfo.szExeFile) == 0)
{
BOOL iswow64 = FALSE;
auto hProcess =
OpenProcess(PROCESS_ALL_ACCESS, FALSE, processInfo.th32ProcessID);
if (hProcess == NULL)
{
LOG_LINE(INFO, "Error on OpenProcess to check bitness");
}
else
{
if (IsWow64Process(hProcess, &iswow64))
{
// LOG_LINE(INFO, "Rocket league process ID is " <<
// processInfo.th32ProcessID << " | " << " has the WOW factor: " <<
// iswow64);
if (!iswow64)
{
CloseHandle(processesSnapshot);
return processInfo.th32ProcessID;
}
}
else
{
LOG_LINE(INFO, "IsWow64Process failed bruv " << GetLastError());
}
CloseHandle(hProcess);
}
}
// CloseHandle(processesSnapshot);
}
CloseHandle(processesSnapshot);
return 0;
}
int wmain(int argc, wchar_t* argv[])
{
DWORD processID;
while (true)
{
processID = GetProcessID64(L"RocketLeague.exe");
if (processID != 0)
break;
Sleep(100);
}
HANDLE h = OpenProcess(PROCESS_ALL_ACCESS, false, processID);
if (h)
{
LPVOID LoadLibAddr = (LPVOID)GetProcAddress(
GetModuleHandleW(L"kernel32.dll"), "LoadLibraryW");
auto ws = L"C:\\users\\steamuser\\Application Data\\bakkesmod\\bakkesmod/dll\\bakkesmod.dll";
auto wslen = (std::wcslen(ws) + 1) * sizeof(WCHAR);
LPVOID dereercomp = VirtualAllocEx(
h, NULL, wslen, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
WriteProcessMemory(h, dereercomp, ws, wslen, NULL);
HANDLE asdc = CreateRemoteThread(
h,
NULL,
NULL,
(LPTHREAD_START_ROUTINE)LoadLibAddr,
dereercomp,
0,
NULL);
WaitForSingleObject(asdc, INFINITE);
DWORD res = 0;
GetExitCodeThread(asdc, &res);
LOG_LINE(INFO, "GetExitCodeThread(): " << (int)res);
LOG_LINE(INFO, "Last error: " << GetLastError());
VirtualFreeEx(h, dereercomp, wslen, MEM_RELEASE);
CloseHandle(asdc);
CloseHandle(h);
return res == 0;
}
return 1;
}
@blastrock
Copy link
Author

blastrock commented Apr 18, 2021

First of all, you need proton-ge-custom or wine-tkg-git proton to use bakkesmod on linux. I use Proton-6.5-GE-2.

You may need to edit the hard coded path for ws to point to the correct dll. It should just be a matter of changing the user name.

Compile with

x86_64-w64-mingw32-g++ inject.cpp -municode -mconsole -lpsapi -std=c++17 -o inject.exe -static

Then run with

WINEESYNC=1 \
WINEPREFIX=/home/blastrock/Games/lutris/epic-games-store \
~/Games/Proton-6.5-GE-2/dist/bin/wine inject.exe

Of course you need to fix the path to Proton's wine, the path to the WINEPREFIX. WINEESYNC depends on how you launch wine for the game itself.

@alanbarnett
Copy link

Understandable if it doesn't matter too much/is hard to work around, but on Linux I get two warnings when compiling. Could you write the code to avoid those? It would be better practice, no? At least, when I was learning C, we were failed for having warnings. (-Werr turns all warnings into errors, and we were failed for any errors.) Is writing around these warnings unnecessary? This a genuine question about style and how you wrote this. Did you ignore the warnings because they were stupid, and it's clear to humans what you meant, and the warnings mean seriously nothing? Or, have you just ignored them? Personally I think it would be better if it were written in such a way that there were no warnings, but having only a scholarly background I wonder if people who code in the real world ignore redundant warnings.

Here are the warnings that I am referring to.

inject.cpp:15:75: warning: passing NULL to non-pointer argument 2 of ‘void* CreateToolhelp32Snapshot(DWORD, DWORD)’ [-Wconversion-null]
   15 |   HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
      |                                                                           ^~~~
In file included from inject.cpp:3:
/usr/x86_64-w64-mingw32/include/tlhelp32.h:15:62: note:   declared here
   15 |   HANDLE WINAPI CreateToolhelp32Snapshot(DWORD dwFlags,DWORD th32ProcessID);
      |                                                        ~~~~~~^~~~~~~~~~~~~
inject.cpp: In function ‘int wmain(int, wchar_t**)’:
inject.cpp:121:9: warning: passing NULL to non-pointer argument 3 of ‘void* CreateRemoteThread(HANDLE, LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD)’ [-Wconversion-null]
  121 |         NULL,
      |         ^~~~
In file included from /usr/x86_64-w64-mingw32/include/winbase.h:29,
                 from /usr/x86_64-w64-mingw32/include/windows.h:70,
                 from inject.cpp:1:
/usr/x86_64-w64-mingw32/include/processthreadsapi.h:108:114: note:   declared here
  108 |   WINBASEAPI HANDLE WINAPI CreateRemoteThread (HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId);
      |

@blastrock
Copy link
Author

I didn't actually write that. I initially wanted to debug the official bakkesmod injector, and to understand why it didn't work, I tried copy pasting the code from the injector so that I could edit and debug it.

This code is a straight copy paste from https://github.com/bakkesmodorg/BakkesModInjectorCpp/blob/06749bc1064da608df5ab44da185aaf9ab546342/BakkesModInjectorC++/BakkesModInjectorCpp.cpp and https://github.com/bakkesmodorg/BakkesModInjectorCpp/blob/06749bc1064da608df5ab44da185aaf9ab546342/BakkesModInjectorC++/DllInjector.cpp IIRC. (clang-format changed the indentation though)

In the end, I didn't edit or debug it, it worked right away, so I can't understand why the official injector doesn't. So this program is more like a proof that the exact code bakkesmod uses should definitely work and there's something else that prevents it. I still have the hope that the official injector gets fixed and that I delete this gist ^^

Then, to answer your question, in my opinion, warnings should be fixed, I definitely like it when there are none. Even if these warnings are harmless, I'd fix them in a serious project. But since in the end this has just become a hack to load bakkesmod, I don't bother maintaining it as it just works. I would argue that these warnings should be fixed in the upstream project. Also, I'm lazy ^^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment