diff --git a/Dalamud.Boot/Dalamud.Boot.vcxproj b/Dalamud.Boot/Dalamud.Boot.vcxproj
index ea263d7f9..dd3f57632 100644
--- a/Dalamud.Boot/Dalamud.Boot.vcxproj
+++ b/Dalamud.Boot/Dalamud.Boot.vcxproj
@@ -32,6 +32,9 @@
obj\$(Configuration)\
+
+
+
true
$(SolutionDir)bin\lib\$(Configuration)\libMinHook\;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64)
@@ -70,6 +73,7 @@
false
false
+ module.def
@@ -83,9 +87,13 @@
true
true
+ module.def
+
+
+
nethost.dll
@@ -181,6 +189,12 @@
+
+
+
+
+
+
diff --git a/Dalamud.Boot/Dalamud.Boot.vcxproj.filters b/Dalamud.Boot/Dalamud.Boot.vcxproj.filters
index 8b4483684..a1b1650e2 100644
--- a/Dalamud.Boot/Dalamud.Boot.vcxproj.filters
+++ b/Dalamud.Boot/Dalamud.Boot.vcxproj.filters
@@ -147,4 +147,14 @@
+
+
+ Dalamud.Boot DLL
+
+
+
+
+ Dalamud.Boot DLL
+
+
\ No newline at end of file
diff --git a/Dalamud.Boot/dllmain.cpp b/Dalamud.Boot/dllmain.cpp
index 2566016e8..cf31b7016 100644
--- a/Dalamud.Boot/dllmain.cpp
+++ b/Dalamud.Boot/dllmain.cpp
@@ -159,7 +159,7 @@ DWORD WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) {
return 0;
}
-DllExport DWORD WINAPI Initialize(LPVOID lpParam) {
+extern "C" DWORD WINAPI Initialize(LPVOID lpParam) {
return InitializeImpl(lpParam, CreateEvent(nullptr, TRUE, FALSE, nullptr));
}
diff --git a/Dalamud.Boot/module.def b/Dalamud.Boot/module.def
new file mode 100644
index 000000000..047d825e5
--- /dev/null
+++ b/Dalamud.Boot/module.def
@@ -0,0 +1,5 @@
+LIBRARY Dalamud.Boot
+EXPORTS
+ Initialize @1
+ RewriteRemoteEntryPointW @2
+ RewrittenEntryPoint @3
diff --git a/Dalamud.Boot/pch.h b/Dalamud.Boot/pch.h
index 3302a44fb..6dda9d03b 100644
--- a/Dalamud.Boot/pch.h
+++ b/Dalamud.Boot/pch.h
@@ -61,9 +61,6 @@
#include "unicode.h"
-// Commonly used macros
-#define DllExport extern "C" __declspec(dllexport)
-
// Global variables
extern HMODULE g_hModule;
extern HINSTANCE g_hGameInstance;
diff --git a/Dalamud.Boot/rewrite_entrypoint.cpp b/Dalamud.Boot/rewrite_entrypoint.cpp
index 85a3a950b..a47254701 100644
--- a/Dalamud.Boot/rewrite_entrypoint.cpp
+++ b/Dalamud.Boot/rewrite_entrypoint.cpp
@@ -5,111 +5,87 @@
DWORD WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue);
struct RewrittenEntryPointParameters {
- void* pAllocation;
char* pEntrypoint;
- char* pEntrypointBytes;
size_t entrypointLength;
- char* pLoadInfo;
- HANDLE hMainThread;
- HANDLE hMainThreadContinue;
};
-#pragma pack(push, 1)
-struct EntryPointThunkTemplate {
- struct DUMMYSTRUCTNAME {
- struct {
- const uint8_t op_mov_rdi[2]{ 0x48, 0xbf };
- void* ptr = nullptr;
- } fn;
+namespace thunks {
+ constexpr uint64_t Terminator = 0xCCCCCCCCCCCCCCCCu;
+ constexpr uint64_t Placeholder = 0x0606060606060606u;
+
+ extern "C" void EntryPointReplacement();
+ extern "C" void RewrittenEntryPoint_Standalone();
- const uint8_t op_call_rdi[2]{ 0xff, 0xd7 };
- } CallTrampoline;
-};
+ void* resolve_thunk_address(void (*pfn)()) {
+ const auto ptr = reinterpret_cast(pfn);
+ if (*ptr == 0xe9)
+ return ptr + 5 + *reinterpret_cast(ptr + 1);
+ return ptr;
+ }
-struct TrampolineTemplate {
- const struct {
- const uint8_t op_sub_rsp_imm[3]{ 0x48, 0x81, 0xec };
- const uint32_t length = 0x80;
- } stack_alloc;
+ size_t get_thunk_length(void (*pfn)()) {
+ size_t length = 0;
+ for (auto ptr = reinterpret_cast(resolve_thunk_address(pfn)); *reinterpret_cast(ptr) != Terminator; ptr++)
+ length++;
+ return length;
+ }
- struct DUMMYSTRUCTNAME {
- struct {
- const uint8_t op_mov_rcx_imm[2]{ 0x48, 0xb9 };
- void* val = nullptr;
- } lpLibFileName;
+ template
+ void* fill_placeholders(void* pfn, const T& value) {
+ auto ptr = static_cast(pfn);
- struct {
- const uint8_t op_mov_rdi_imm[2]{ 0x48, 0xbf };
- decltype(&LoadLibraryW) ptr = nullptr;
- } fn;
+ while (*reinterpret_cast(ptr) != Placeholder)
+ ptr++;
- const uint8_t op_call_rdi[2]{ 0xff, 0xd7 };
- } CallLoadLibrary_nethost;
+ *reinterpret_cast(ptr) = 0;
+ *reinterpret_cast(ptr) = value;
+ return ptr + sizeof(value);
+ }
- struct DUMMYSTRUCTNAME {
- struct {
- const uint8_t op_mov_rcx_imm[2]{ 0x48, 0xb9 };
- void* val = nullptr;
- } lpLibFileName;
+ template
+ void* fill_placeholders(void* ptr, const T& value, TArgs&&...more_values) {
+ return fill_placeholders(fill_placeholders(ptr, value), std::forward(more_values)...);
+ }
- struct {
- const uint8_t op_mov_rdi_imm[2]{ 0x48, 0xbf };
- decltype(&LoadLibraryW) ptr = nullptr;
- } fn;
+ std::vector create_entrypointreplacement() {
+ std::vector buf(get_thunk_length(&EntryPointReplacement));
+ memcpy(buf.data(), resolve_thunk_address(&EntryPointReplacement), buf.size());
+ return buf;
+ }
- const uint8_t op_call_rdi[2]{ 0xff, 0xd7 };
- } CallLoadLibrary_DalamudBoot;
+ std::vector create_standalone_rewrittenentrypoint(const std::filesystem::path& dalamud_path) {
+ const auto nethost_path = std::filesystem::path(dalamud_path).replace_filename(L"nethost.dll");
- struct {
- const uint8_t hModule_op_mov_rcx_rax[3]{ 0x48, 0x89, 0xc1 };
+ // These are null terminated, since pointers are returned from .c_str()
+ const auto dalamud_path_wview = std::wstring_view(dalamud_path.c_str());
+ const auto nethost_path_wview = std::wstring_view(nethost_path.c_str());
- struct {
- const uint8_t op_mov_rdx_imm[2]{ 0x48, 0xba };
- void* val = nullptr;
- } lpProcName;
+ // +2 is for null terminator
+ const auto dalamud_path_view = std::span(reinterpret_cast(dalamud_path_wview.data()), dalamud_path_wview.size() * 2 + 2);
+ const auto nethost_path_view = std::span(reinterpret_cast(nethost_path_wview.data()), nethost_path_wview.size() * 2 + 2);
- struct {
- const uint8_t op_mov_rdi_imm[2]{ 0x48, 0xbf };
- decltype(&GetProcAddress) ptr = nullptr;
- } fn;
+ std::vector buffer;
+ const auto thunk_template_length = thunks::get_thunk_length(&thunks::RewrittenEntryPoint_Standalone);
+ buffer.reserve(thunk_template_length + dalamud_path_view.size() + nethost_path_view.size());
+ buffer.resize(thunk_template_length);
+ memcpy(buffer.data(), resolve_thunk_address(&thunks::RewrittenEntryPoint_Standalone), thunk_template_length);
- const uint8_t op_call_rdi[2]{ 0xff, 0xd7 };
- } CallGetProcAddress;
+ // &::GetProcAddress will return Dalamud.dll's import table entry.
+ // GetProcAddress(..., "GetProcAddress") returns the address inside kernel32.dll.
+ const auto kernel32 = GetModuleHandleA("kernel32.dll");
- struct {
- const uint8_t op_add_rsp_imm[3]{ 0x48, 0x81, 0xc4 };
- const uint32_t length = 0x80;
- } stack_release;
-
- struct DUMMYSTRUCTNAME2 {
- // rdi := returned value from GetProcAddress
- const uint8_t op_mov_rdi_rax[3]{ 0x48, 0x89, 0xc7 };
- // rax := return address
- const uint8_t op_pop_rax[1]{ 0x58 };
-
- // rax := rax - sizeof thunk (last instruction must be call)
- struct {
- const uint8_t op_sub_rax_imm4[2]{ 0x48, 0x2d };
- const uint32_t displacement = static_cast(sizeof EntryPointThunkTemplate);
- } op_sub_rax_to_entry_point;
-
- struct {
- const uint8_t op_mov_rcx_imm[2]{ 0x48, 0xb9 };
- void* val = nullptr;
- } param;
-
- const uint8_t op_push_rax[1]{ 0x50 };
- const uint8_t op_jmp_rdi[2]{ 0xff, 0xe7 };
- } CallInjectEntryPoint;
-
- const char buf_CallGetProcAddress_lpProcName[20] = "RewrittenEntryPoint";
- uint8_t buf_EntryPointBackup[sizeof EntryPointThunkTemplate]{};
-
-#pragma pack(push, 8)
- RewrittenEntryPointParameters parameters{};
-#pragma pack(pop)
-};
-#pragma pack(pop)
+ thunks::fill_placeholders(buffer.data(),
+ /* pfnLoadLibraryW = */ GetProcAddress(kernel32, "LoadLibraryW"),
+ /* pfnGetProcAddress = */ GetProcAddress(kernel32, "GetProcAddress"),
+ /* pRewrittenEntryPointParameters = */ Placeholder,
+ /* nNethostOffset = */ 0,
+ /* nDalamudOffset = */ nethost_path_view.size_bytes()
+ );
+ buffer.insert(buffer.end(), nethost_path_view.begin(), nethost_path_view.end());
+ buffer.insert(buffer.end(), dalamud_path_view.begin(), dalamud_path_view.end());
+ return buffer;
+ }
+}
void read_process_memory_or_throw(HANDLE hProcess, void* pAddress, void* data, size_t len) {
SIZE_T read = 0;
@@ -170,10 +146,17 @@ void* get_mapped_image_base_address(HANDLE hProcess, const std::filesystem::path
exe.read(reinterpret_cast(&exe_section_headers[0]), sizeof IMAGE_SECTION_HEADER * exe_section_headers.size());
if (!exe)
throw std::runtime_error("Game executable is corrupt (Truncated section header).");
+
+ SYSTEM_INFO sysinfo;
+ GetSystemInfo(&sysinfo);
for (MEMORY_BASIC_INFORMATION mbi{};
VirtualQueryEx(hProcess, mbi.BaseAddress, &mbi, sizeof mbi);
mbi.BaseAddress = static_cast(mbi.BaseAddress) + mbi.RegionSize) {
+
+ // wine: apparently there exists a RegionSize of 0xFFF
+ mbi.RegionSize = (mbi.RegionSize + sysinfo.dwPageSize - 1) / sysinfo.dwPageSize * sysinfo.dwPageSize;
+
if (!(mbi.State & MEM_COMMIT) || mbi.Type != MEM_IMAGE)
continue;
@@ -241,18 +224,6 @@ void* get_mapped_image_base_address(HANDLE hProcess, const std::filesystem::path
throw std::runtime_error("corresponding base address not found");
}
-std::string from_utf16(const std::wstring& wstr, UINT codePage = CP_UTF8) {
- std::string str(WideCharToMultiByte(codePage, 0, &wstr[0], static_cast(wstr.size()), nullptr, 0, nullptr, nullptr), 0);
- WideCharToMultiByte(codePage, 0, &wstr[0], static_cast(wstr.size()), &str[0], static_cast(str.size()), nullptr, nullptr);
- return str;
-}
-
-std::wstring to_utf16(const std::string& str, UINT codePage = CP_UTF8, bool errorOnInvalidChars = false) {
- std::wstring wstr(MultiByteToWideChar(codePage, 0, &str[0], static_cast(str.size()), nullptr, 0), 0);
- MultiByteToWideChar(codePage, errorOnInvalidChars ? MB_ERR_INVALID_CHARS : 0, &str[0], static_cast(str.size()), &wstr[0], static_cast(wstr.size()));
- return wstr;
-}
-
/// @brief Rewrite target process' entry point so that this DLL can be loaded and executed first.
/// @param hProcess Process handle.
/// @param pcwzPath Path to target process.
@@ -263,9 +234,9 @@ std::wstring to_utf16(const std::string& str, UINT codePage = CP_UTF8, bool erro
/// Instead, we have to enumerate through all the files mapped into target process' virtual address space and find the base address
/// of memory region corresponding to the path given.
///
-DllExport DWORD WINAPI RewriteRemoteEntryPointW(HANDLE hProcess, const wchar_t* pcwzPath, const wchar_t* pcwzLoadInfo) {
+extern "C" DWORD WINAPI RewriteRemoteEntryPointW(HANDLE hProcess, const wchar_t* pcwzPath, const wchar_t* pcwzLoadInfo) {
try {
- const auto base_address = reinterpret_cast(get_mapped_image_base_address(hProcess, pcwzPath));
+ const auto base_address = static_cast(get_mapped_image_base_address(hProcess, pcwzPath));
IMAGE_DOS_HEADER dos_header{};
union {
@@ -279,60 +250,35 @@ DllExport DWORD WINAPI RewriteRemoteEntryPointW(HANDLE hProcess, const wchar_t*
? nt_header32.OptionalHeader.AddressOfEntryPoint
: nt_header64.OptionalHeader.AddressOfEntryPoint);
- auto path = get_path_from_local_module(g_hModule).wstring();
- path.resize(path.size() + 1); // ensure null termination
- auto path_bytes = std::span(reinterpret_cast(&path[0]), std::span(path).size_bytes());
+ auto standalone_rewrittenentrypoint = thunks::create_standalone_rewrittenentrypoint(get_path_from_local_module(g_hModule));
+ auto entrypoint_replacement = thunks::create_entrypointreplacement();
- auto nethost_path = (get_path_from_local_module(g_hModule).parent_path() / L"nethost.dll").wstring();
- nethost_path.resize(nethost_path.size() + 1); // ensure null termination
- auto nethost_path_bytes = std::span(reinterpret_cast(&nethost_path[0]), std::span(nethost_path).size_bytes());
-
- auto load_info = from_utf16(pcwzLoadInfo);
+ auto load_info = unicode::convert(pcwzLoadInfo);
load_info.resize(load_info.size() + 1); //ensure null termination
- // Allocate full buffer in advance to keep reference to trampoline valid.
- std::vector buffer(sizeof TrampolineTemplate + load_info.size() + nethost_path_bytes.size() + path_bytes.size());
- auto& trampoline = *reinterpret_cast(&buffer[0]);
- const auto load_info_buffer = std::span(buffer).subspan(sizeof trampoline, load_info.size());
- const auto nethost_path_buffer = std::span(buffer).subspan(sizeof trampoline + load_info.size(), nethost_path_bytes.size());
- const auto dalamud_path_buffer = std::span(buffer).subspan(sizeof trampoline + load_info.size() + nethost_path_bytes.size(), path_bytes.size());
-
- new(&trampoline)TrampolineTemplate(); // this line initializes given buffer instead of allocating memory
- memcpy(&load_info_buffer[0], &load_info[0], load_info_buffer.size());
- memcpy(&nethost_path_buffer[0], &nethost_path_bytes[0], nethost_path_buffer.size());
- memcpy(&dalamud_path_buffer[0], &path_bytes[0], dalamud_path_buffer.size());
-
- // Backup remote process' original entry point.
- read_process_memory_or_throw(hProcess, entrypoint, trampoline.buf_EntryPointBackup);
+ std::vector buffer(sizeof(RewrittenEntryPointParameters) + entrypoint_replacement.size() + load_info.size() + standalone_rewrittenentrypoint.size());
// Allocate buffer in remote process, which will be used to fill addresses in the local buffer.
- const auto remote_buffer = reinterpret_cast(VirtualAllocEx(hProcess, nullptr, buffer.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE));
-
- // Fill the values to be used in RewrittenEntryPoint
- trampoline.parameters = {
- .pAllocation = remote_buffer,
- .pEntrypoint = entrypoint,
- .pEntrypointBytes = remote_buffer + offsetof(TrampolineTemplate, buf_EntryPointBackup),
- .entrypointLength = sizeof trampoline.buf_EntryPointBackup,
- .pLoadInfo = remote_buffer + (&load_info_buffer[0] - &buffer[0]),
- };
+ const auto remote_buffer = static_cast(VirtualAllocEx(hProcess, nullptr, buffer.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE));
+
+ auto& params = *reinterpret_cast(buffer.data());
+ params.entrypointLength = entrypoint_replacement.size();
+ params.pEntrypoint = entrypoint;
- // Fill the addresses referred in machine code.
- trampoline.CallLoadLibrary_nethost.lpLibFileName.val = remote_buffer + (&nethost_path_buffer[0] - &buffer[0]);
- trampoline.CallLoadLibrary_nethost.fn.ptr = LoadLibraryW;
- trampoline.CallLoadLibrary_DalamudBoot.lpLibFileName.val = remote_buffer + (&dalamud_path_buffer[0] - &buffer[0]);
- trampoline.CallLoadLibrary_DalamudBoot.fn.ptr = LoadLibraryW;
- trampoline.CallGetProcAddress.lpProcName.val = remote_buffer + offsetof(TrampolineTemplate, buf_CallGetProcAddress_lpProcName);
- trampoline.CallGetProcAddress.fn.ptr = GetProcAddress;
- trampoline.CallInjectEntryPoint.param.val = remote_buffer + offsetof(TrampolineTemplate, parameters);
+ // Backup original entry point.
+ read_process_memory_or_throw(hProcess, entrypoint, &buffer[sizeof params], entrypoint_replacement.size());
+
+ memcpy(&buffer[sizeof params + entrypoint_replacement.size()], load_info.data(), load_info.size());
+
+ thunks::fill_placeholders(standalone_rewrittenentrypoint.data(), remote_buffer);
+ memcpy(&buffer[sizeof params + entrypoint_replacement.size() + load_info.size()], standalone_rewrittenentrypoint.data(), standalone_rewrittenentrypoint.size());
// Write the local buffer into the buffer in remote process.
write_process_memory_or_throw(hProcess, remote_buffer, buffer.data(), buffer.size());
- // Overwrite remote process' entry point with a thunk that immediately calls our trampoline function.
- EntryPointThunkTemplate thunk{};
- thunk.CallTrampoline.fn.ptr = remote_buffer;
- write_process_memory_or_throw(hProcess, entrypoint, thunk);
+ thunks::fill_placeholders(entrypoint_replacement.data(), remote_buffer + sizeof params + entrypoint_replacement.size() + load_info.size());
+ // Overwrite remote process' entry point with a thunk that will load our DLLs and call our trampoline function.
+ write_process_memory_or_throw(hProcess, entrypoint, entrypoint_replacement.data(), entrypoint_replacement.size());
return 0;
} catch (const std::exception& e) {
@@ -341,44 +287,43 @@ DllExport DWORD WINAPI RewriteRemoteEntryPointW(HANDLE hProcess, const wchar_t*
}
}
-/// @deprecated
-DllExport DWORD WINAPI RewriteRemoteEntryPoint(HANDLE hProcess, const wchar_t* pcwzPath, const char* pcszLoadInfo) {
- return RewriteRemoteEntryPointW(hProcess, pcwzPath, to_utf16(pcszLoadInfo).c_str());
+static void AbortRewrittenEntryPoint(DWORD err, const std::wstring& clue) {
+ wchar_t* pwszMsg = nullptr;
+ FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr,
+ err,
+ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
+ reinterpret_cast(&pwszMsg),
+ 0,
+ nullptr);
+
+ if (MessageBoxW(nullptr, std::format(
+ L"Failed to load Dalamud. Load game without Dalamud(yes) or abort(no)?\n\nError: 0x{:08X} {}\n\n{}",
+ err, pwszMsg ? pwszMsg : L"", clue).c_str(),
+ L"Dalamud.Boot", MB_OK | MB_YESNO) == IDNO)
+ ExitProcess(-1);
}
/// @brief Entry point function "called" instead of game's original main entry point.
/// @param params Parameters set up from RewriteRemoteEntryPoint.
-DllExport void WINAPI RewrittenEntryPoint(RewrittenEntryPointParameters& params) {
- params.hMainThreadContinue = CreateEventW(nullptr, true, false, nullptr);
- if (!params.hMainThreadContinue)
- ExitProcess(-1);
+extern "C" void WINAPI RewrittenEntryPoint_AdjustedStack(RewrittenEntryPointParameters & params) {
+ const auto pOriginalEntryPointBytes = reinterpret_cast(¶ms) + sizeof(params);
+ const auto pLoadInfo = pOriginalEntryPointBytes + params.entrypointLength;
- // Do whatever the work in a separate thread to minimize the stack usage at this context,
- // as this function really should have been a naked procedure but __declspec(naked) isn't supported in x64 version of msvc.
- params.hMainThread = CreateThread(nullptr, 0, [](void* p) -> DWORD {
- try {
- std::string loadInfo;
- auto& params = *reinterpret_cast(p);
- {
- // Restore original entry point.
- // Use WriteProcessMemory instead of memcpy to avoid having to fiddle with VirtualProtect.
- write_process_memory_or_throw(GetCurrentProcess(), params.pEntrypoint, params.pEntrypointBytes, params.entrypointLength);
+ // Restore original entry point.
+ // Use WriteProcessMemory instead of memcpy to avoid having to fiddle with VirtualProtect.
+ if (SIZE_T written; !WriteProcessMemory(GetCurrentProcess(), params.pEntrypoint, pOriginalEntryPointBytes, params.entrypointLength, &written))
+ return AbortRewrittenEntryPoint(GetLastError(), L"WriteProcessMemory(entrypoint restoration)");
- // Make a copy of load info, as the whole params will be freed after this code block.
- loadInfo = params.pLoadInfo;
- }
+ const auto hMainThreadContinue = CreateEventW(nullptr, true, false, nullptr);
+ if (!hMainThreadContinue)
+ return AbortRewrittenEntryPoint(GetLastError(), L"CreateEventW");
- InitializeImpl(&loadInfo[0], params.hMainThreadContinue);
- return 0;
- } catch (const std::exception& e) {
- MessageBoxA(nullptr, std::format("Failed to load Dalamud.\n\nError: {}", e.what()).c_str(), "Dalamud.Boot", MB_OK | MB_ICONERROR);
- ExitProcess(-1);
- }
- }, ¶ms, 0, nullptr);
- if (!params.hMainThread)
- ExitProcess(-1);
+ if (const auto result = InitializeImpl(pLoadInfo, hMainThreadContinue))
+ return AbortRewrittenEntryPoint(result, L"InitializeImpl");
- CloseHandle(params.hMainThread);
- WaitForSingleObject(params.hMainThreadContinue, INFINITE);
- VirtualFree(params.pAllocation, 0, MEM_RELEASE);
+ WaitForSingleObject(hMainThreadContinue, INFINITE);
+ VirtualFree(¶ms, 0, MEM_RELEASE);
}
diff --git a/Dalamud.Boot/rewrite_entrypoint_thunks.asm b/Dalamud.Boot/rewrite_entrypoint_thunks.asm
new file mode 100644
index 000000000..af7be8287
--- /dev/null
+++ b/Dalamud.Boot/rewrite_entrypoint_thunks.asm
@@ -0,0 +1,82 @@
+PUBLIC EntryPointReplacement
+PUBLIC RewrittenEntryPoint_Standalone
+PUBLIC RewrittenEntryPoint
+
+; 06 and 07 are invalid opcodes
+; CC is int3 = bp
+; using 0CCCCCCCCCCCCCCCCh as function terminator
+; using 00606060606060606h as placeholders
+
+TERMINATOR = 0CCCCCCCCCCCCCCCCh
+PLACEHOLDER = 00606060606060606h
+
+.code
+
+EntryPointReplacement PROC
+ start:
+ ; rsp % 0x10 = 0x08
+ lea rax, [start]
+ push rax
+
+ ; rsp % 0x10 = 0x00
+ mov rax, PLACEHOLDER
+
+ ; this calls RewrittenEntryPoint_Standalone
+ jmp rax
+
+ dq TERMINATOR
+EntryPointReplacement ENDP
+
+RewrittenEntryPoint_Standalone PROC
+ start:
+ ; stack is aligned to 0x10; see above
+ sub rsp, 20h
+ lea rcx, [embeddedData]
+ add rcx, qword ptr [nNethostOffset]
+ call qword ptr [pfnLoadLibraryW]
+
+ lea rcx, [embeddedData]
+ add rcx, qword ptr [nDalamudOffset]
+ call qword ptr [pfnLoadLibraryW]
+
+ mov rcx, rax
+ lea rdx, [pcszEntryPointName]
+ call qword ptr [pfnGetProcAddress]
+
+ mov rcx, qword ptr [pRewrittenEntryPointParameters]
+ ; this calls RewrittenEntryPoint
+ jmp rax
+
+ pfnLoadLibraryW:
+ dq PLACEHOLDER
+
+ pfnGetProcAddress:
+ dq PLACEHOLDER
+
+ pRewrittenEntryPointParameters:
+ dq PLACEHOLDER
+
+ nNethostOffset:
+ dq PLACEHOLDER
+
+ nDalamudOffset:
+ dq PLACEHOLDER
+
+ pcszEntryPointName:
+ db "RewrittenEntryPoint", 0
+
+ embeddedData:
+
+ dq TERMINATOR
+RewrittenEntryPoint_Standalone ENDP
+
+EXTERN RewrittenEntryPoint_AdjustedStack :PROC
+
+RewrittenEntryPoint PROC
+ ; stack is aligned to 0x10; see above
+ call RewrittenEntryPoint_AdjustedStack
+ add rsp, 20h
+ ret
+RewrittenEntryPoint ENDP
+
+END