Welcome!

Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!

Join Today!

[Release] Fixing Bad Image error on Windows 11

Newbie Spellweaver
Joined
Nov 15, 2022
Messages
20
Reaction score
9
Hello :)

I've seen a lot of people having trouble running some maple versions on Windows 11.
The affected versions I've seen seem to be 32bit versions above v176, but more versions could be affected.

I ran into the same problem when trying to run v207.

The Problem

Some time after launching the game, I get the following error:
shailist - [Release] Fixing Bad Image error on Windows 11 - RaGEZONE Forums


It took some research, but what seems to happen is that maple is copying DLLs into a temp path and then loads them from there.
The code that does that is virtualized so I didn't bother trying to understand it.
What seems to happen is that in some cases, the game tries to load the copied DLL before the copying process finishes (how do you even code something this garbage?? wth nexon).

The Fix

If maple is copying the DLLs and then loading them, lets just make it load the original DLL.

1. The first step is to keep track of files that are being copied:
PHP:
std::mutex coped_files_mutex;
std::unordered_map<std::wstring, std::wstring> copied_files;

WINBASEAPI
BOOL
WINAPI
BasepCopyFileExW(
    IN LPCWSTR lpExistingFileName,
    IN LPCWSTR lpNewFileName,
    ...
);

void BasepCopyFileExW_hook_impl(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName)
{
    if ((nullptr != lpExistingFileName) && (nullptr != lpNewFileName))
    {
        std::unique_lock copied_dlls_lock(coped_files_mutex);
        copied_files[lpNewFileName] = lpExistingFileName;
    }
}

__declspec(naked)
BOOL
WINAPI
BasepCopyFileExW_hook(
    IN LPCWSTR lpExistingFileName,
    IN LPCWSTR lpNewFileName,
    ...
)
{
    BasepCopyFileExW_hook_impl(lpExistingFileName, lpNewFileName);

    __asm {
        jmp original_BasepCopyFileExW
    }
}


(Don't mind the crappy BasepCopyFileExW signature and jmp to the original function, I can't seem to get the function signature right :(:)

2. The second step is to make every try of loading a copied DLL load the original one:
PHP:
NTSTATUS
NTAPI
LdrLoadDll_hook(
    PWSTR search_path OPTIONAL,    PULONG dll_characteristics OPTIONAL,    PUNICODE_STRING dll_name,
    PVOID* base_address
)
{
    if ((nullptr != dll_name) && (nullptr != dll_name->Buffer))
    {
        auto* begin = dll_name->Buffer;
        auto* end = reinterpret_cast<PWSTR>(reinterpret_cast<PBYTE>(begin) + dll_name->Length);
        std::wstring dll_path(begin, end);

        std::wstring original_dll;
        {
            std::unique_lock copied_dlls_lock(coped_files_mutex);
            original_dll = copied_files[dll_path];
        }

        if (!original_dll.empty())
        {
            *base_address = LoadLibraryW(original_dll.c_str());
            return STATUS_SUCCESS;
        }
    }

    return original_LdrLoadDll(search_path, dll_characteristics, dll_name, base_address);
}

Hope this helps someone :)
 
Last edited:
Newbie Spellweaver
Joined
Jul 26, 2022
Messages
13
Reaction score
1
Hey started to get this error, how and where do I implement this fix?
 
Junior Spellweaver
Joined
Aug 13, 2009
Messages
124
Reaction score
123
Here's the unvirtualized code, it is trying to make 5~10 copies of ws2_32.dll from the system directory to the Windows temporary directory. Renamed to "nstXXXX.tmp", and finally to load the cloned DLL.

It was originally implemented just before big-bang to prevent API hooks on the network adapters against private servers using Microsoft Loopback Adapter.
Unfortunately that broke shortly after Windows 8, and on Windows 10. Stopping this usually fixes windows compatibility right away.

Code:
HMODULE sub_9AC411()
{
  char *v2; // [esp+Ch] [ebp-488h]
  unsigned int v3; // [esp+10h] [ebp-484h]
  char *v5; // [esp+28h] [ebp-46Ch]
  int v6[13]; // [esp+2Ch] [ebp-468h] BYREF
  unsigned int v7; // [esp+60h] [ebp-434h]
  DWORD NumberOfBytesRead; // [esp+128h] [ebp-36Ch] BYREF
  __int16 v9[30]; // [esp+12Ch] [ebp-368h] BYREF
  LONG lDistanceToMove; // [esp+168h] [ebp-32Ch]
  HANDLE hFile; // [esp+16Ch] [ebp-328h]
  CHAR PathName[264]; // [esp+174h] [ebp-320h] BYREF
  char v13[263]; // [esp+27Ch] [ebp-218h] BYREF
  char v14; // [esp+383h] [ebp-111h] BYREF
  CHAR Buffer[268]; // [esp+384h] [ebp-110h] BYREF

  GetSystemDirectoryA(Buffer, 0x104u);
  strcpy(v13, "ws2_32.dll");
  v5 = &v14;
  while ( *++v5 )
    ;
  *(_WORD *)v5 = word_CA9E7C;
  v3 = strlen(v13) + 1;
  v2 = &v14;
  while ( *++v2 )
    ;
  qmemcpy(v2, v13, v3);
  GetTempPathA(0x104u, PathName);
  GetTempFileNameA(PathName, PrefixString, 0, PathName);
  CopyFileA(Buffer, PathName, 0);
  hFile = CreateFileA(PathName, 0xC0000000, 3u, 0, 3u, 0x80u, 0);
  if ( hFile != (HANDLE)-1 )
  {
    ReadFile(hFile, v9, 0x40u, &NumberOfBytesRead, 0);
    if ( v9[0] == 23117 )
    {
      SetFilePointer(hFile, lDistanceToMove, 0, 0);
      ReadFile(hFile, v6, 0xF8u, &NumberOfBytesRead, 0);
      if ( v6[0] == 17744 && v7 > 0x80000000 )
      {
        v7 = 0x10000000;
        SetFilePointer(hFile, lDistanceToMove, 0, 0);
        WriteFile(hFile, v6, 0xF8u, &NumberOfBytesRead, 0);
      }
    }
    CloseHandle(hFile);
  }
  return LoadLibraryExA(PathName, 0, 8u);
}
 
Last edited:
Newbie Spellweaver
Joined
Nov 15, 2022
Messages
20
Reaction score
9
Here's the unvirtualized code, it is trying to make 5~10 copies of ws2_32.dll from the system directory to the Windows temporary directory. Renamed to "nstXXXX.tmp", and finally to load the cloned DLL.

It was originally implemented just before big-bang to prevent API hooks on the network adapters against private servers using Microsoft Loopback Adapter.
Unfortunately that broke shortly after Windows 8, and on Windows 10. Stopping this usually fixes windows compatibility right away.

Code:
HMODULE sub_9AC411()
{
  char *v2; // [esp+Ch] [ebp-488h]
  unsigned int v3; // [esp+10h] [ebp-484h]
  char *v5; // [esp+28h] [ebp-46Ch]
  int v6[13]; // [esp+2Ch] [ebp-468h] BYREF
  unsigned int v7; // [esp+60h] [ebp-434h]
  DWORD NumberOfBytesRead; // [esp+128h] [ebp-36Ch] BYREF
  __int16 v9[30]; // [esp+12Ch] [ebp-368h] BYREF
  LONG lDistanceToMove; // [esp+168h] [ebp-32Ch]
  HANDLE hFile; // [esp+16Ch] [ebp-328h]
  CHAR PathName[264]; // [esp+174h] [ebp-320h] BYREF
  char v13[263]; // [esp+27Ch] [ebp-218h] BYREF
  char v14; // [esp+383h] [ebp-111h] BYREF
  CHAR Buffer[268]; // [esp+384h] [ebp-110h] BYREF

  GetSystemDirectoryA(Buffer, 0x104u);
  strcpy(v13, "ws2_32.dll");
  v5 = &v14;
  while ( *++v5 )
    ;
  *(_WORD *)v5 = word_CA9E7C;
  v3 = strlen(v13) + 1;
  v2 = &v14;
  while ( *++v2 )
    ;
  qmemcpy(v2, v13, v3);
  GetTempPathA(0x104u, PathName);
  GetTempFileNameA(PathName, PrefixString, 0, PathName);
  CopyFileA(Buffer, PathName, 0);
  hFile = CreateFileA(PathName, 0xC0000000, 3u, 0, 3u, 0x80u, 0);
  if ( hFile != (HANDLE)-1 )
  {
    ReadFile(hFile, v9, 0x40u, &NumberOfBytesRead, 0);
    if ( v9[0] == 23117 )
    {
      SetFilePointer(hFile, lDistanceToMove, 0, 0);
      ReadFile(hFile, v6, 0xF8u, &NumberOfBytesRead, 0);
      if ( v6[0] == 17744 && v7 > 0x80000000 )
      {
        v7 = 0x10000000;
        SetFilePointer(hFile, lDistanceToMove, 0, 0);
        WriteFile(hFile, v6, 0xF8u, &NumberOfBytesRead, 0);
      }
    }
    CloseHandle(hFile);
  }
  return LoadLibraryExA(PathName, 0, 8u);
}
lmao, doesn't even prevent net hooking
 
Initiate Mage
Joined
May 30, 2023
Messages
2
Reaction score
0
hello , I also encountered this problem

How can I use these codes to solve the problem
 
Back
Top