2

I started a new project recently and my objectives is to inject bytecode into another process, and then start a remote thread executing my bytecode, however i have run into a very strange problem.

What it does is that it allocates and write to memory of a arbitrary process, it writes a struct containing pointers to functions in user32.dll and kernel32.dll for the remote process, it also writes a calling operation for the function pointers from the struct, it then creates a remotethread with a lpStartAddress of the "calling operation"

You can find the source code here : http://pastie.org/9298306

GetPrivileges is being called on line 55 (method on line 185), it returns true meaning that OpenProcessToken, LookupPrivilegeValue and AdjustTokenPrivileges returned true.

Soon after that the following will be callled:

param->pMessageBox = (DWORD)GetProcAddress(user32, "MessageBoxA");
param->pSleep = (DWORD)GetProcAddress(kernel32, "Sleep");

Both user32 and kernel32 are valid handles, but param->pMessageBox will be set to NULL, whilst param->pSleep will get the actual pointer for the Sleep.

And the strange thing about this is when i replace the GetPrivileges with this snippet that i copied online it works fine and the param->pMessageBox will be set with the correct pointer address.

BOOL GetPrivileges()
{
  HANDLE tokenHandle;
  TOKEN_PRIVILEGES tokenPriv;

  if(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &tokenHandle) != 0)
  {
    LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tokenPriv.Privileges[0].Luid);
    tokenPriv.PrivilegeCount = 1;
    tokenPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    AdjustTokenPrivileges(tokenHandle, 0, &tokenPriv, sizeof(tokenPriv), NULL, NULL);
  }
  else
  {
    TCHAR buffer[256];    
    wsprintf(buffer, TEXT("0x%x"), GetLastError());
    MessageBox(NULL, buffer, TEXT("OpenProcessTokenError"), MB_ICONERROR);

    return FALSE;
  }

  return true;
}

Continuing on with my debugging take note that the else statement in the copied GetPrivileges will not be called due to OpenProcessToken returning true as expected, and by removing:

TCHAR buffer[256];    
wsprintf(buffer, TEXT("0x%x"), GetLastError());

param->pMessageBox will set to NULL, how can that be?

Regards a frustrated ogelami.

Ogelami
  • 366
  • 1
  • 14
  • 2
    This appears to be calling `GetProcAddress` in the local process, using a module handle from the remote process. This makes no sense, though it will work at random if the modules happen to be loaded at the same address in both processes. – arx Jun 17 '14 at 09:45
  • This answers my question, thanks alot why did it even work in the first place with the wsprintf? was it the same offset for the dlls in both my process and the target process? would that be a reasonable explanation? – Ogelami Jun 17 '14 at 11:11
  • PE-Images specify a *preferred load address*. Loading a binary image into different processes is somewhat likely to be placed at the same virtual address in either one (which is the same as having the same `HMODULE`). That's what @arx described by the phrase *"will work at random"*. – IInspectable Jun 17 '14 at 11:20
  • @Ogelami If you are removing the call to `MessageBox` along with the `wsprintf` then `user32` might not be loaded at all in the local process, so `GetProcAddress` would definitely fail. Also be aware of [ASLR](http://en.wikipedia.org/wiki/Address_space_layout_randomization). – arx Jun 17 '14 at 12:00
  • You also might want to checkout the [Detours](https://www.microsoft.com/en-us/research/project/detours/) library from Microsoft Research. They give Win32 away and license x64. I tried to get an x64 license years ago but it never materialized. I don't recall what went wrong with licensing the x64 gear. About all I remember is I sent the fax. – jww Oct 10 '16 at 03:47
  • Pastie link is down, might want to remove it at this point – GuidedHacking Aug 27 '18 at 02:31

1 Answers1

2

The module handles are in fact not valid. They are module handles for a remote process. Module handles are in fact implement as base addresses and so only have meaning with respect to the virtual address space of the executing process.

It looks like, by chance, the base address of the kernel32 module of the injecting process is the same as the base address of the kernel32 module in the remote process.

Realistically, your goals are going to be hard to achieve if you put so much code in the injecting process. You would be better off if you injected a DLL into the other process. Create a remote thread whose first act is to load this DLL. Then you will have code running in the other process, inside its address space, and so able to call directly functions like GetModuleHandle, GetProcAddress etc.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • It's my understanding that kernel32 is always going to be at the same base address for all processes (of a given bitness, I assume) so that you can determine the remote addresses of the most essential functions (LoadLibrary, GetProcAddress) in order to call them in your injected thread. Otherwise you're in a chicken-and-egg situation. – Harry Johnston Jun 18 '14 at 01:50
  • @Harry I'm not sure that's always true. Do you have any docs that say so? – David Heffernan Jun 18 '14 at 06:03
  • We know that [starting a remote thread pointed at the LoadLibrary function](http://stackoverflow.com/questions/18941843/why-does-createremotethread-work-here) works; the most likely explanation (though not the only possible one) is that kernel32 is at the same location for all processes. The only MS documentation I can find [is this](http://msdn.microsoft.com/en-us/library/bb430720.aspx) which does seem to state that the addresses are only randomized at boot. There are various SO questions and answers such as [this one](http://stackoverflow.com/a/6396342/886887) but I admit to some doubt. – Harry Johnston Jun 19 '14 at 01:59
  • I'm not aware of any way of injecting code into a remote process that doesn't depend on at least one function (typically LoadLibrary or GetProcAddress) being in the same location as it is in your own process, so if you're going to use CreateRemoteThread at all I believe you have no choice but to assume that these particular functions are safe. As a precaution, I'd recommend that addresses for all other functions, even those in kernel32, be obtained legitimately. – Harry Johnston Jun 19 '14 at 02:16
  • @harry I inject into a process of my own a different way. I inject to be able to dump debugging info in case of deadlock. I have the process write an address to a memory mapped file. Then the injector opens that file, reads the address, and injects. Requires cooperation. – David Heffernan Jun 19 '14 at 05:34
  • This [GetProcAddressEx](https://guidedhacking.com/threads/how-to-find-module-function-address-externally.10439/post-57591) function can get the function address externally at run time. But judging by these comments, that didn't seem to be the root cause of the issue after all... – GuidedHacking Aug 27 '18 at 02:26