Overriding DLL imports in FlashpointServer.exe

Writing the DLL

The first step is to write a DLL which exports a function that will replace CreateFileA(). This function needs to be written in such a way that it passes all its arguments through to the "real" CreateFileA() unless the file being created is a sound file, in which case tests are done to determine if the call should be allowed to continue.

Since the replacement function will be called every time a file is opened for reading or writing, it needs to be efficient. Ideally it would be written in assembly.

The replacement code I used is a C function called furrycat_CreateFileA(), with its definition borrowed from the SDK.

__declspec(dllexport) HANDLE WINAPI furrycat_CreateFileA(IN LPCSTR lpFileName, IN DWORD dwDesiredAccess, IN DWORD dwShareMode, IN LPSECURITY_ATTRIBUTES lpSecurityAttributes, IN DWORD dwCreationDisposition, IN DWORD dwFlagsAndAttributes, IN HANDLE hTemplateFile) {
  unsigned long i;
  char *path;

  /* Only interested in WRITING files */
  if (! (dwDesiredAccess & GENERIC_WRITE)) goto passthrough;

  /* Only interested in writing to sounds directory */
  i = is_sounds_dir(lpFileName);
  if (! i) goto passthrough;

  /* Allocate memory for copy of path name */
  path = (char *) HeapAlloc(GetProcessHeap(), 0, i);
  if (! path) goto passthrough;

  /* Copy path */
  CopyMemory(path, lpFileName, i);
  path[i - 1] = '\0';

  /* Check the directory */
  if (under_limit(path, max_size)) goto cleanup_passthrough;

  /* Cleanup */
  HeapFree(GetProcessHeap(), 0, path);

  /* Deny */
  return INVALID_HANDLE_VALUE;

cleanup_passthrough:
  /* Cleanup */
  HeapFree(GetProcessHeap(), 0, path);

passthrough:
  return CreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
}

We want to get out of the function (by calling the real CreateFileA()) as quickily as possible. We first check if we are supposed to be writing to a file and pass through if not. No processing is needing for reading files.

The server writes other file apart from sounds. We know that sounds are written to the tmp2303\players\playername\sound directory for some value of playername. The is_sounds_dir() function returns the number of bytes needed to store the path to the player's sound directory, or 0 if the target file is not such a sound file.

If we're still in the function at this point, we make a copy of the path and pass this copy to the under_limit() function, which returns a non-zero value if the user hasn't exceeded his quota and hence should be allowed to save this sound file. Based on the function's return code, we decide whether to process the CreateFileA() request or return an error.

At time of writing, the file size limit is 35Kb. It's easy to test the function by tacking on a main() which tries to write a file whose name is specified by argv[1]. You can then create subdirectories of tmp2303\players and fill them up with files...

The exported function

You can use dumpbin or some other tool to find out the exported functions decorated name (if any). I compiled the DLL as soundsfix.dll and saw that the function got named:

_furrycat_CreateFileA@28


Jump to a section

intro | part 1: Creating the DLL | part 2: Editing the PE header

Download soundsfix 1.91 code and source.