Calling Win32 APIs from VBA
Windows operating system APIs are located in dynamic link
libraries and run as unmanaged code.
We’ll use the Declare keyword to link to these APIs in VBA,
providing the name of the function, the DLL it resides in,
the argument types, and return value types.
We’ll use a Private Declare,
meaning that the function will only be used in our local code.
In this example, we’ll use the GetUserName API.
We’ll build our declare function statement,
and display the username in a popup with MsgBox.
The official documentation provided by Microsoft on MSDN
contains the function prototype.
The documentation tells us the maximum size of the username,
along with the DLL it resides in.
We can expand on that to declare the function we want.
The function arguments are described on MSDN as native C types
and we must translate these to their corresponding VBA data types.
The first argument is an output buffer of C type LPSTR
which will contain the current username.
It can be supplied as a String in VBA.
In C, the LPSTR is a pointer to a string.
Similarily, the VBA String object holds the the pointer to a string,
rather than the string itself.
For this reason we can pass our argument by value,
with ByVal, since the expected types match.
The second argument, pcbBuffer, given in the function prototype as a C type,
is a pointer or reference to an DWORD.
It is the maximum size of the buffer that will contain the string.
We may substitute that with the VBA Long data type
and pass it by reference with ByRef, to obtain a pointer in VBA.
Finally the output type in C is a boolean, which we can translate into a Long in VBA.
Now that we have explained all the components, let’s put it all together.
We’ll import our target function using Private Declare,
and supply the Windows API name and its DLL location,
along with our arguments.
This declare statement must be placed outside the procedure.
With the function imported, we must declare three variables;
the return value,
the output buffer,
and the size of the output buffer.
The maximum allowed length of a username is 256 characters
so we’ll create a 256-byte String called MyBuff
and a variable called MySize as a Long and set it to 256.
Before we can print the result, remember that MyBuff can contain up to
256 characters but we do not know the length of the actual username.
Since a C string is terminated by a null byte,
we’ll use the InStr function
to get the index of a null byte terminator in the buffer,
which marks the end of the string.
The arguments for InStr are fairly straightforward.
We define the starting location by setting it to “1” for the beginning of the string,
the string to search, and the search character, in this case a null byte.
This will return the location of the first null byte,
and we can subtract one from this number to get the string length.
Now that we have the length of the string,
we’ll print the non-null characters by using the the Left method.
Left creates a substring of its first argument with the size of its second argument.
If we’ve called the Win32 API correctly,
the macro will display the desired username, with no trailing spaces.
While this is obviously only a proof of concept,
it shows that we can call arbitrary Win32 APIs directly from VBA,
which is required if we want to execute shellcode from memory.