metasploit-framework/lib/metasm/samples/r0trace.rb

309 lines
10 KiB
Ruby

# This file is part of Metasm, the Ruby assembly manipulation suite
# Copyright (C) 2006-2009 Yoann GUILLOT
#
# Licence is LGPL, see LICENCE in the top-level directory
# This creates/uses a ring0 driver for tracing a program
# x86/windows/singlecore only
# the scripts allows interacting with the driver
# you still have to set the target thread in singlestep mode (eg using Debugger)
# How does it work:
# the driver hooks the IDT int1/int0f (NOT SMP PROOF)
# on int1, it logs eip in a memory buffer
# on int0f, it returns the memory buffer (memcopy to mem pointed by eax)
# the buffer 1st dword is the number of used dwords in the buffer (0 = empty)
# on overflow, eips are lost
require 'metasm'
include Metasm
$drv = 'r0trace.sys'
# size of the eip buffer (in dwords)
TRACE_BUF_SZ = 4*1024*1024-4
if not File.exist? $drv
PE.assemble(Ia32.new, <<EOS).encode_file($drv, 'kmod')
#define bufsz #{TRACE_BUF_SZ}
.data
oldi1 dd 0,0
oldi15 dd 0,0
buf dd bufsz dup(?)
.text
.entrypoint
mov eax, [esp+4]
mov dword ptr [eax+0x34], unload // drv->DriverUnload
call setup_idt
xor eax, eax
mov [buf], eax // buf used size
ret
unload:
// XXX smp
call get_idt
push [oldi1+4]
push [oldi1]
pop [eax+8*1]
pop [eax+8*1+4]
push [oldi15+4]
push [oldi15]
pop [eax+8*15]
pop [eax+8*15+4]
xor eax, eax
ret
setup_idt:
// XXX smp
call get_idt
push [eax+8*1]
push [eax+8*1+4]
pop [oldi1+4]
pop [oldi1]
push [eax+8*15]
push [eax+8*15+4]
pop [oldi15+4]
pop [oldi15]
mov ecx, i1hook
mov [eax+8*1], ecx
mov [eax+8*1+4], ecx
mov ecx, cs
mov [eax+8*1+2], cx
mov word ptr [eax+8*1+4], (15 + (3<<5) + (1<<7)) << 8 // call gate
mov ecx, i15hook
mov [eax+8*15], ecx
mov [eax+8*15+4], ecx
mov ecx, cs
mov [eax+8*15+2], cx
mov word ptr [eax+8*15+4], (15 + (3<<5) + (1<<7)) << 8 // call gate
ret
get_idt:
sub esp, 8
sidt [esp]
mov eax, [esp+2]
add esp, 8
ret
i1hook:
push eax
mov eax, buf
cmp [eax], bufsz
jae 1f
inc [eax]
add eax, [eax]
push [esp+4]
pop [eax]
1:
pop eax
iret
i15hook:
push esi
push edi
push ecx
mov esi, buf
mov edi, eax
mov ecx, [esi]
inc ecx
rep movsd
mov dword ptr [buf], 0
pop ecx
pop edi
pop esi
iret
EOS
end
DynLdr.new_api_c <<EOS
typedef int BOOL;
typedef char CHAR;
typedef unsigned long DWORD;
typedef const CHAR *LPCSTR;
typedef DWORD *LPDWORD;
typedef void *HANDLE;
struct SC_HANDLE__ { int unused; };
struct _SERVICE_STATUS {
DWORD dwServiceType;
DWORD dwCurrentState;
DWORD dwControlsAccepted;
DWORD dwWin32ExitCode;
DWORD dwServiceSpecificExitCode;
DWORD dwCheckPoint;
DWORD dwWaitHint;
};
typedef struct SC_HANDLE__ *SC_HANDLE;
typedef struct _SERVICE_STATUS *LPSERVICE_STATUS;
__stdcall BOOL CloseServiceHandle(SC_HANDLE hSCObject __attribute__((in)));
__stdcall SC_HANDLE CreateServiceA(SC_HANDLE hSCManager __attribute__((in)), LPCSTR lpServiceName __attribute__((in)), LPCSTR lpDisplayName __attribute__((in)), DWORD dwDesiredAccess __attribute__((in)), DWORD dwServiceType __attribute__((in)), DWORD dwStartType __attribute__((in)), DWORD dwErrorControl __attribute__((in)), LPCSTR lpBinaryPathName __attribute__((in)), LPCSTR lpLoadOrderGroup __attribute__((in)), LPDWORD lpdwTagId __attribute__((out)), LPCSTR lpDependencies __attribute__((in)), LPCSTR lpServiceStartName __attribute__((in)), LPCSTR lpPassword __attribute__((in)));
__stdcall BOOL DeleteService(SC_HANDLE hService __attribute__((in)));
__stdcall SC_HANDLE OpenSCManagerA(LPCSTR lpMachineName __attribute__((in)), LPCSTR lpDatabaseName __attribute__((in)), DWORD dwDesiredAccess __attribute__((in)));
__stdcall SC_HANDLE OpenServiceA(SC_HANDLE hSCManager __attribute__((in)), LPCSTR lpServiceName __attribute__((in)), DWORD dwDesiredAccess __attribute__((in)));
__stdcall BOOL StartServiceA(SC_HANDLE hService __attribute__((in)), DWORD dwNumServiceArgs __attribute__((in)), LPCSTR *lpServiceArgVectors __attribute__((in)));
__stdcall BOOL ControlService(SC_HANDLE hService __attribute__((in)), DWORD dwControl __attribute__((in)), LPSERVICE_STATUS lpServiceStatus __attribute__((out)));
__stdcall HANDLE OpenThread(DWORD dwDesiredAccess __attribute__((in)), BOOL bInheritHandle __attribute__((in)), DWORD dwThreadId __attribute__((in)));
__stdcall DWORD ResumeThread(HANDLE hThread __attribute__((in)));
__stdcall DWORD SuspendThread(HANDLE hThread __attribute__((in)));
__stdcall BOOL SetThreadContext(HANDLE hThread __attribute__((in)), void *lpContext __attribute__((in)));
__stdcall BOOL GetThreadContext(HANDLE hThread __attribute__((in)), void *lpContext);
__stdcall BOOL CloseHandle(HANDLE hObject __attribute__((in)));
#define STANDARD_RIGHTS_REQUIRED (0x000F0000L)
#define SC_MANAGER_CONNECT 0x0001
#define SC_MANAGER_CREATE_SERVICE 0x0002
#define SC_MANAGER_ENUMERATE_SERVICE 0x0004
#define SC_MANAGER_LOCK 0x0008
#define SC_MANAGER_QUERY_LOCK_STATUS 0x0010
#define SC_MANAGER_MODIFY_BOOT_CONFIG 0x0020
#define SC_MANAGER_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | \
SC_MANAGER_CONNECT | \
SC_MANAGER_CREATE_SERVICE | \
SC_MANAGER_ENUMERATE_SERVICE | \
SC_MANAGER_LOCK | \
SC_MANAGER_QUERY_LOCK_STATUS | \
SC_MANAGER_MODIFY_BOOT_CONFIG)
#define SERVICE_QUERY_CONFIG 0x0001
#define SERVICE_CHANGE_CONFIG 0x0002
#define SERVICE_QUERY_STATUS 0x0004
#define SERVICE_ENUMERATE_DEPENDENTS 0x0008
#define SERVICE_START 0x0010
#define SERVICE_STOP 0x0020
#define SERVICE_PAUSE_CONTINUE 0x0040
#define SERVICE_INTERROGATE 0x0080
#define SERVICE_USER_DEFINED_CONTROL 0x0100
#define SERVICE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | \
SERVICE_QUERY_CONFIG | \
SERVICE_CHANGE_CONFIG | \
SERVICE_QUERY_STATUS | \
SERVICE_ENUMERATE_DEPENDENTS | \
SERVICE_START | \
SERVICE_STOP | \
SERVICE_PAUSE_CONTINUE | \
SERVICE_INTERROGATE | \
SERVICE_USER_DEFINED_CONTROL)
#define SERVICE_KERNEL_DRIVER 0x00000001
#define SERVICE_FILE_SYSTEM_DRIVER 0x00000002
#define SERVICE_ADAPTER 0x00000004
#define SERVICE_RECOGNIZER_DRIVER 0x00000008
#define SERVICE_DRIVER (SERVICE_KERNEL_DRIVER | \
SERVICE_FILE_SYSTEM_DRIVER | \
SERVICE_RECOGNIZER_DRIVER)
#define SERVICE_WIN32_OWN_PROCESS 0x00000010
#define SERVICE_WIN32_SHARE_PROCESS 0x00000020
#define SERVICE_WIN32 (SERVICE_WIN32_OWN_PROCESS | \
SERVICE_WIN32_SHARE_PROCESS)
#define SERVICE_INTERACTIVE_PROCESS 0x00000100
#define SERVICE_TYPE_ALL (SERVICE_WIN32 | \
SERVICE_ADAPTER | \
SERVICE_DRIVER | \
SERVICE_INTERACTIVE_PROCESS)
#define SERVICE_BOOT_START 0x00000000
#define SERVICE_SYSTEM_START 0x00000001
#define SERVICE_AUTO_START 0x00000002
#define SERVICE_DEMAND_START 0x00000003
#define SERVICE_DISABLED 0x00000004
#define SERVICE_ERROR_IGNORE 0x00000000
#define SERVICE_ERROR_NORMAL 0x00000001
#define SERVICE_ERROR_SEVERE 0x00000002
#define SERVICE_ERROR_CRITICAL 0x00000003
#define SYNCHRONIZE 0x00100000L
#define THREAD_TERMINATE 0x0001
#define THREAD_SUSPEND_RESUME 0x0002
#define THREAD_GET_CONTEXT 0x0008
#define THREAD_SET_CONTEXT 0x0010
#define THREAD_SET_INFORMATION 0x0020
#define THREAD_QUERY_INFORMATION 0x0040
#define THREAD_SET_THREAD_TOKEN 0x0080
#define THREAD_IMPERSONATE 0x0100
#define THREAD_DIRECT_IMPERSONATION 0x0200
#define THREAD_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3FF)
#define CONTEXT_i386 0x00010000
#define CONTEXT_CONTROL (CONTEXT_i386 | 0x00000001L) // SS:SP, CS:IP, FLAGS, BP
EOS
DynLdr.new_func_c <<EOS
int get_trace_buf(void *ptr)
{
asm("mov eax, [ebp+8] int 0fh");
return *(int*)ptr;
}
EOS
def loadmod(mod=$drv)
sh = DynLdr.openscmanagera(0, 0, DynLdr::SC_MANAGER_ALL_ACCESS)
raise "cannot openscm" if (sh == 0)
rh = DynLdr.createservicea(sh, mod, mod, DynLdr::SERVICE_ALL_ACCESS, DynLdr::SERVICE_KERNEL_DRIVER, DynLdr::SERVICE_DEMAND_START, DynLdr::SERVICE_ERROR_NORMAL, File.expand_path(mod), 0, 0, 0, 0, 0)
if (DynLdr.startservicea(rh, 0, 0) == 0)
raise "cannot start service"
end
DynLdr.CloseServiceHandle(rh)
DynLdr.CloseServiceHandle(sh)
end
def unloadmod(mod=$drv)
sh = DynLdr.openscmanagera(0, 0, DynLdr::SC_MANAGER_ALL_ACCESS)
raise "cannot openscm" if (sh == 0)
rh = DynLdr.openservicea(sh, mod, DynLdr::SERVICE_ALL_ACCESS)
DynLdr.controlservice(rh, DynLdr::SERVICE_CONTROL_STOP, 0.chr*4*32)
DynLdr.deleteservice(rh)
DynLdr.CloseServiceHandle(rh)
DynLdr.CloseServiceHandle(sh)
end
def trace(tid, delay=1)
# put thread in singlestep mode
th = DynLdr.openthread(DynLdr::THREAD_GET_CONTEXT | DynLdr::THREAD_SET_CONTEXT | DynLdr::THREAD_SUSPEND_RESUME, 0, tid)
raise "openthread" if (th == 0)
DynLdr.suspendthread(th)
ctx = 0.chr * 1024
ctx[0, 4] = [DynLdr::CONTEXT_CONTROL].pack('V')
DynLdr.getthreadcontext(th, ctx)
ctx[192, 4] = [ctx[192, 4].unpack('V').first | (1 << 8)].pack('V')
DynLdr.setthreadcontext(th, ctx)
DynLdr.resumethread(th)
DynLdr.closehandle(th)
buf = 0.chr * 4 * TRACE_BUF_SZ
loop do
sleep delay.to_f
nr = DynLdr.get_trace_buf(buf)
puts "got #{'%x' % nr} instrs"
# eips = buf[4, 4*nr].unpack('V*')
end
ensure
th = DynLdr.openthread(DynLdr::THREAD_GET_CONTEXT | DynLdr::THREAD_SET_CONTEXT | DynLdr::THREAD_SUSPEND_RESUME, 0, tid)
if (th != 0)
DynLdr.suspendthread(th)
ctx = 0.chr * 1024
ctx[0, 4] = [DynLdr::CONTEXT_CONTROL].pack('V')
DynLdr.getthreadcontext(th, ctx)
ctx[192, 4] = [ctx[192, 4].unpack('V').first & ~(1 << 8)].pack('V')
DynLdr.setthreadcontext(th, ctx)
DynLdr.resumethread(th)
DynLdr.closehandle(th)
end
end
if $0 == __FILE__
case ARGV.shift
when /unload/; unloadmod(*ARGV)
when /load/; loadmod(*ARGV)
when /trace/; trace(*ARGV)
end
end