mirror of https://github.com/n1nj4sec/pupy.git
100 lines
4.3 KiB
C
100 lines
4.3 KiB
C
|
//#include "common.h"
|
||
|
#include <windows.h>
|
||
|
#include "remote_thread.h"
|
||
|
/*! @brief Container structure for a client identifer used when creating remote threads with RtlCreateUserThread. */
|
||
|
typedef struct _MIMI_CLIENT_ID {
|
||
|
PVOID UniqueProcess;
|
||
|
PVOID UniqueThread;
|
||
|
} CLIENTID;
|
||
|
|
||
|
/*! @brief Function pointer type for the RtlCreateUserThread function in ntdll.dll */
|
||
|
typedef NTSTATUS (WINAPI * PRtlCreateUserThread)(HANDLE, PSECURITY_DESCRIPTOR, BOOL, ULONG, SIZE_T, SIZE_T, PTHREAD_START_ROUTINE, PVOID, PHANDLE, CLIENTID*);
|
||
|
/*! @brief Reference to the loaded RtlCreateUserThread function pointer. */
|
||
|
static PRtlCreateUserThread pRtlCreateUserThread = NULL;
|
||
|
/*! @brief Indication of whether an attempt to locate the pRtlCreateUserThread pointer has been made. */
|
||
|
static BOOL pRtlCreateUserThreadAttempted = FALSE;
|
||
|
|
||
|
/*!
|
||
|
* @brief Helper function for creating a remote thread in a privileged process.
|
||
|
* @param hProcess Handle to the target process.
|
||
|
* @param sStackSize Size of the stack to use (if unsure, specify 0).
|
||
|
* @param pvStartAddress Pointer to the function entry point that has been loaded into the target.
|
||
|
* @param pvStartParam Pointer to the parameter to pass to the thread function.
|
||
|
* @param dwCreateFlags Creation flags to use when creating the new thread.
|
||
|
* @param pdwThreadId Pointer to the buffer that will receive the thread ID (optional).
|
||
|
* @return Handle to the new thread.
|
||
|
* @retval NULL Indicates an error, which can be retrieved with \c GetLastError().
|
||
|
* @remark This function has been put in place to wrap up the handling of creating remote threads
|
||
|
* in privileged processes across all operating systems. In Windows XP and earlier, the
|
||
|
* \c CreateRemoteThread() function was sufficient to handle this case, however this changed
|
||
|
* in Vista and has been that way since. For Vista onwards, the use of the hidden API function
|
||
|
* \c RtlCreateUserThread() is required. This function attempts to use \c CreateRemoteThread()
|
||
|
* first and if that fails it will fall back to \c RtlCreateUserThread(). This means that the
|
||
|
* existing behaviour is kept for when running on XP and earlier, or when the user is already
|
||
|
* running within a privileged process.
|
||
|
*/
|
||
|
HANDLE create_remote_thread(HANDLE hProcess, SIZE_T sStackSize, LPVOID pvStartAddress, LPVOID pvStartParam, DWORD dwCreateFlags, LPDWORD pdwThreadId)
|
||
|
{
|
||
|
NTSTATUS ntResult;
|
||
|
BOOL bCreateSuspended;
|
||
|
DWORD dwThreadId;
|
||
|
HANDLE hThread;
|
||
|
|
||
|
if (pdwThreadId == NULL)
|
||
|
{
|
||
|
pdwThreadId = &dwThreadId;
|
||
|
}
|
||
|
|
||
|
hThread = CreateRemoteThread(hProcess, NULL, sStackSize, (LPTHREAD_START_ROUTINE)pvStartAddress, pvStartParam, dwCreateFlags, pdwThreadId);
|
||
|
|
||
|
// ERROR_NOT_ENOUGH_MEMORY is returned when the function fails due to insufficient privs
|
||
|
// on Vista and later.
|
||
|
if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY)
|
||
|
{
|
||
|
//dprintf("[REMOTETHREAD] CreateRemoteThread seems to lack permissions, trying alternative options");
|
||
|
hThread = NULL;
|
||
|
|
||
|
// Only attempt to load the function pointer if we haven't attempted it already.
|
||
|
if (!pRtlCreateUserThreadAttempted)
|
||
|
{
|
||
|
if (pRtlCreateUserThread == NULL)
|
||
|
{
|
||
|
pRtlCreateUserThread = (PRtlCreateUserThread)GetProcAddress(GetModuleHandleA("ntdll"), "RtlCreateUserThread");
|
||
|
if (pRtlCreateUserThread)
|
||
|
{
|
||
|
//dprintf("[REMOTETHREAD] RtlCreateUserThread found at %p, using for backup remote thread creation", pRtlCreateUserThread);
|
||
|
}
|
||
|
}
|
||
|
pRtlCreateUserThreadAttempted = TRUE;
|
||
|
}
|
||
|
|
||
|
// if at this point we don't have a valid pointer, it means that we don't have this function available
|
||
|
// on the current OS
|
||
|
if (pRtlCreateUserThread)
|
||
|
{
|
||
|
DWORD (WINAPI *fGetThreadId)(HANDLE Thread);
|
||
|
fGetThreadId = (void*)GetProcAddress(GetModuleHandleA("kernel32"), "GetThreadId");
|
||
|
if(fGetThreadId){
|
||
|
//dprintf("[REMOTETHREAD] Attempting thread creation with RtlCreateUserThread");
|
||
|
bCreateSuspended = (dwCreateFlags & CREATE_SUSPENDED) == CREATE_SUSPENDED;
|
||
|
ntResult = pRtlCreateUserThread(hProcess, NULL, bCreateSuspended, 0, 0, 0, (PTHREAD_START_ROUTINE)pvStartAddress, pvStartParam, &hThread, NULL);
|
||
|
SetLastError(ntResult);
|
||
|
|
||
|
if (ntResult == 0 && pdwThreadId)
|
||
|
{
|
||
|
*pdwThreadId = fGetThreadId(hThread);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// restore the previous error so that it looks like we haven't done anything else
|
||
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hThread;
|
||
|
}
|
||
|
|
||
|
|