Accessing the Windows API Directly

If you are into pentesting I am sure you might have heard about the IRB shell in the Metasploit framework. This will be a small post about accessing Windows API using Railgun. Using Railgun we can access different functions in DLLs during runtime in memory. We could also write our own DLLs and call them directly using Railgun. This technique is used in the Meterpreter scripts and post exploitation modules to access the API to perform automated tasks.
For demonstration I will be using a Windows 7 machine as the target and Kali as the attacker machine.
After owning the box in the meterpreter session type “irb” and from there we can start the interactive ruby shell. The “client” will be our meterpreter client. We can access common API calls like this. Suppose I want to get the system information.


Get the user ID


View post on

Get all network interfaces. To verify the return type of the object type .class at the end. In this case it’s an array.

init =
init.each { |x| puts x.pretty }

View post on

The above are built-in calls. Using Railgun we can access the Windows API directly. The syntax would be.

Client.railgun.(DLL).(function)(arg 1, arg 2, …)

I will demonstrate some examples.
So suppose I want to access the MessageBox function in the Windows API. It’s located in the “user32” DLL.

int WINAPI MessageBox(
  _In_opt_  HWND hWnd,
  _In_opt_  LPCTSTR lpText,
  _In_opt_  LPCTSTR lpCaption,
  _In_      UINT uType

To call the function we can type:

?> client.railgun.user32.MessageBoxA(0, "Hello World", "Osanda", "MB_ICONASTERISK | MB_OK" )
=> {"GetLastError"=>0, "ErrorMessage"=>"The operation completed successfully.", "return"=>1}

View post on

If you want to lock the workstation you could use “LockWorkStation” API.
BOOL WINAPI LockWorkStation(void);

?> client.railgun.user32.LockWorkStation()
=> {"GetLastError"=>0, "ErrorMessage"=>"The operation completed successfully.", "return"=>true}

View post on

Suppose I want to terminate a process. For that I will be using the “OpenProcess” and “TerminateProcess” functions.

  _In_  DWORD dwDesiredAccess,
  _In_  BOOL bInheritHandle,
  _In_  DWORD dwProcessId

BOOL WINAPI TerminateProcess(
  _In_  HANDLE hProcess,
  _In_  UINT uExitCode

If I want to terminate the CMD running in the target machine. I’ll first get the handle to “PROCESS_TERMINATE” and store the return value in a variable and next call “TerminateProcess” API to terminate the process.

?> client.railgun.kernel32.OpenProcess("PROCESS_TERMINATE", false, 3664)
=> {"GetLastError"=>0, "ErrorMessage"=>"The operation completed successfully.", "return"=>4692}
>> phandle = _['return']
=> 4692
>> client.railgun.kernel32.TerminateProcess(phandle, 0)
=> {"GetLastError"=>0, "ErrorMessage"=>"The operation completed successfully.", "return"=>true}

Likewise you could do cool stuff by directly accessing the API during runtime. Read the prototype of the function and apply accordingly using Railgun.
If you want to find the functions loaded to Railgun in a specific DLL just get the exception error message and you will see the functions loaded.

View post on

Now let’s try to add a new DLL which is not shipped by default into Railgun. To check the available DLL type.

?> client.railgun.known_dll_names
=> ["kernel32", "ntdll", "user32", "ws2_32", "iphlpapi", "advapi32", "shell32", "netapi32", "crypt32", "wlanapi", "wldap32", "version"]

Let’s try to add “mpr.dll” into Railgun at runtime and try to access a function. This would be the syntax.

client.railgun.add_dll(Name, Path)

To add “mpr.dll” we can enter like this:

client.railgun.add_dll("mpr", "C:/windows/system32/mpr.dll")

After that you should add the function. To view the functions of a DLL I will be using DLL Export Viewer by Nirsoft, feel free to use any utility you like.

View post on

I would like to use the “WNetGetUserW” function. Let’s check the function from MSDN.

DWORD WNetGetUser(
  _In_     LPCTSTR lpName,
  _Out_    LPTSTR lpUserName,
  _Inout_  LPDWORD lpnLength

We should follow the syntax of the Railgun.

client.railgun.add_function("mpr", "WNetGetUserW", "DWORD", [
["PWCHAR", "lpName", "in"],
["PWCHAR", "lpUserName", "out"],
["PDWORD", "lpnLength", "inout"]

After adding the function we can run the function passing the arguments 🙂

>> client.railgun.mpr.WNetGetUserW(nil,50,50)
=> {"GetLastError"=>0, "ErrorMessage"=>"The operation completed successfully.", "return"=>0, "username"=>"SYSTEM\x00AAAAAAAAAAAAAAAAAA", "lplen"=>50}

That is how you can access the Windows API using Railgun. For more info about editing modules read their documentation
MSDN is your friend. 🙂 To play around with different APIs apply according to information provided by MSDN.

Thanks for reading.

3 thoughts on “Accessing the Windows API Directly

Leave a Reply