软件沙箱技术–安全分析沙箱CuckooSandbox
1.Cockoo的功能
Cockoo Sandbox是开源安全沙箱,基于GPLv3。⽬的是恶意软件(malware analysis)分析。使⽤的时候将待分析⽂件丢到沙箱内,分析结束后输出报告。很多安全设备提供商所谓云沙箱是同类技术,⼀些所谓Anti-APT产品也是这个概念。和传统AV软件的静态分析相
⽐,Cuckoo动态检测。扔到沙箱的可执⾏⽂件会被执⾏,⽂档会被打开,运⾏中检测。和不少开源软件学⽽优则仕⼀样,Cookoo搞了⼀个产品,。这种⽅式也是挺好的⼀种做开源软件的运营⽅式,Snort的SourceFire不就是被思科⼗⼏亿收购了吗。
Cuckoo它可以分析的内容有:
1.Windows可执⾏⽂件,DLL⽂件
2.PDF,MSOffice⽂档
3.URL和HTML⽂档
4.PHP脚本,VB脚本
5.CPL⽂件
6.ZIP⽂件,Jar⽂件
7.Python⽂件
可以看出,只是Windows平台,外加⼀些脚本和⽂件。
Cuckoo的分析结果包含如下内容:
1.函数以及API调⽤的Call Trace
2.应⽤拷贝和删除的⽂件
3.选定进程的内存镜像
4.分析机的full memory dump
5.恶意软件执⾏时的截屏
6.分析机产⽣的⽹络流量
2.部署和使⽤
下图是Cuckoo的部署,其实很简单,分为host和guests。
Host(管理机)
负责
管理guests
启动分析⼯作
⽹络流量收集等。
host依赖⼀些开源软件,例如
tcpdump⽤于Guest⽹络拦截
Volatility⽤于内存的dump
Guest(虚拟机)
Guest是通⽤的虚拟机,Xen、VirtualBox等。它运⾏Cuckoo的Agent,接收Host发过来的任务(⽂件)运⾏后获取信息。
Agent是跨平台的(就是Python脚本)可以运⾏在Windows、Linux和MAC OS上。它实际是是⼀个XMLRPC server,等待连接。
Cuckoo使⽤
Cuckoo提供了python命令⾏⼯具。
1.使⽤cuckoo.py启动引擎。
2.使⽤submit.py像cuckoo提交待分析应⽤。
3.引擎会和虚拟机中的Agent通信,运⾏应⽤。
4.分析结束后,结果输出到特定⽬录。
3.Hook引擎
cuckoo使⽤了通⽤虚拟机做Guest,本⾝也没有隔离机制和访问控制机制。它特殊的地⽅就是Hook机制。
核⼼模块
cuckoomon(Cuckoo Sandbox Monitor)的源码在。它的作⽤是Hook在可执⾏程序上,拦截执⾏流程。 形式上它是⼀个DLL,会被inject到待分析的恶意软件和它创建的所有进程中。所谓动态分析,就是Hook后的API,在线收集的运⾏时信息。主要有两个步骤:
3.1 DLL注⼊
注⼊是使⽤Python实现的,流程如下:
1.使⽤CreateProcess(CREATE SUSPENDED
CREATE SUSPENDED)启动应⽤
2.使⽤CreateRemoteThread和QueueUserAPC API调⽤LoadLibrary注⼊cuckoomon.dll。
3.Resume进程的主线程
参照下⾯代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26class Process :
"" "Windows process." ""
def inject ( self , dll = os . path . join ( "dll" , "cuckoomon.dll" ) , apc = False ) : "" "Cuckoo DLL injection.
@param dll: Cuckoo DLL path.
@param apc: APC use.
" ""
if self . pid == 0 :
log . warning ( "No valid pid specified, injection aborted" )
return False
if not self . is_alive ( ) :
log . warning ( "The process with pid %d is not alive, injection "
"aborted" % self . pid )
return False
dll = randomize_dll ( dll )
if not dll or not os . path . exists ( dll ) :
log . warning ( "No valid DLL specified to be injected in process "
"with pid %d, injection aborted" % self . pid )
return False
arg = KERNEL32 . VirtualAllocEx ( self . h_process ,
None ,
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 len ( dll ) + 1 ,
MEM_RESERVE | MEM_COMMIT ,
PAGE_READWRITE )
if not arg :
log . error ( "VirtualAllocEx failed when injecting process with "
"pid %d, injection aborted (Error: %s)"
% ( self . pid , get_error_string ( KERNEL32 . GetLastError ( ) ) ) ) return False
bytes_written = c_int ( 0 )
if not KERNEL32 . WriteProcessMemory ( self . h_process ,
arg ,
dll + "\x00" ,
len ( dll ) + 1 ,
byref ( bytes_written ) ) :
log . error ( "WriteProcessMemory failed when injecting process "
"with pid %d, injection aborted (Error: %s)"
% ( self . pid , get_error_string ( KERNEL32 . GetLastError ( ) ) ) ) return False
kernel32_handle = KERNEL32 . GetModuleHandleA ( "kernel32.dll" )
load_library = KERNEL32 . GetProcAddress ( kernel32_handle ,
"LoadLibraryA" )
config_path = os . path . join ( os . getenv ( "TEMP" ) , "%s.ini" % self . pid ) with open ( config_path , "w" ) as config :
cfg = Config ( "f" )
config . write ( "host-ip={0}\n" . format ( cfg . ip ) )
config . write ( "host-port={0}\n" . format ( cfg . port ) )
config . write ( "pipe={0}\n" . format ( PIPE ) )
config . write ( "results={0}\n" . format ( PATHS [ "root" ] ) )
config . write ( "analyzer={0}\n" . format ( os . getcwd ( ) ) )
config . write ( "first-process={0}\n" . format ( Process . first_process ) )
Process . first_process = False
if apc or self . suspended :
log . info ( "Using QueueUserAPC injection" )
if not self . h_thread :
log . info ( "No valid thread handle specified for injecting "
"process with pid %d, injection aborted" % self . pid )
return False
if not KERNEL32 . QueueUserAPC ( load_library , self . h_thread , arg ) : log . error ( "QueueUserAPC failed when injecting process "
"with pid %d (Error: %s)"
% ( self . pid ,
get_error_string ( KERNEL32 . GetLastError ( ) ) ) )
return False
log . info ( "Successfully injected process with pid %d" % self . pid )
else :
event_name = "CuckooEvent%d" % self . pid
self . event_handle = KERNEL32 . CreateEventA ( None ,
False ,
False ,
event_name )
if not self . event_handle :
log . warning ( "Unable to create notify event.." )
return False
log . info ( "Using CreateRemoteThread injection" )
new_thread_id = c_ulong ( 0 )
thread_handle = KERNEL32 . CreateRemoteThread ( self . h_process ,
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 None ,
0 ,
load_library ,
arg ,
0 ,
byref ( new_thread_id ) )
if not thread_handle :
log . error ( "CreateRemoteThread failed when injecting " + "process with pid %d (Error: %s)" % ( self . pid ,
get_error_string ( KERNEL32 . GetLastError ( ) ) ) )
KERNEL32 . CloseHandle ( self . event_handle )
self . event_handle = None
return False
else :
KERNEL32 . CloseHandle ( thread_handle )
return True
3.2 API Hook
thread技术1.上⼀步中主线程恢复后,因为APC callback,Cuckoo Monitor⾸先被执⾏。
2.初始化并且安装Hook
Cuckoo对ntdll.dll, kernel32.dll, advapi32.dll,shell32.dll,msvcrt.dll,user32.dll,wininet.dll,ws2_32.dll,mswsock.dll中的170+API进⾏hook
3.通知分析模块Analyzer(通过命名管道),应⽤启动。
4.Log将会通过实现配置好的TCP/IP端⼝发送给Host。
下⾯是添加Hook的代码:
int hook_api(hook_t *h, int type) { // table with all possible hooking types static struct { int(*hook)(hook_t *h, unsigned char *from, unsigned char *to); int len; } hook_types[] = { /* HOOK_JMP_DIRECT */ {&hook_api_jmp_direct, 5}, /* HOOK_NOP_JMP_DIRECT */ {&hook_api_nop_jmp_direct, 6}, /* HOOK_HOTPATCH_JMP_DIRECT */ {&hook_api_hotpatch_jmp_direct, 7}, /* HOOK_PUSH_RETN */ {&hook_api_push_retn, 6}, /* HOOK_NOP_PUSH_RETN */ {&hook_api_nop_push_retn, 7}, /* HOOK_JMP_INDIRECT */ {&hook_api_jmp_indirect, 6}, /* HOOK_MOV_EAX_JMP_EAX */ {&hook_api_mov_eax_jmp_eax, 7}, /* HOOK_MOV_EAX_PUSH_RETN */ {&hook_api_mov_eax_pus
h_retn, 7}, /* HOOK_MOV_EAX_INDIRECT_JMP_EAX */ {&hook_api_mov_eax_indirect_jmp_eax, 7}, /* HOOK_MOV_EAX_INDIRECT_PUSH_RETN */ {&hook_api_mov_eax_indirect_push_retn, 7}, #if HOOK_ENABLE_FPU /* HOOK_PUSH_FPU_RETN */ {&hook_api_push_fpu_retn, 11}, #endif /* HOOK_SPECIAL_JMP */ {&hook_api_special_jmp, 7}, }; // is this address already hooked? if(h->is_hooked != 0) { return 0; } // resolve the address to hook unsigned char *addr = h->addr; if(addr == NULL && h->library != NULL && h->funcname != NULL) { addr = (unsigned char *) GetProcAddress(GetModuleHandleW(h->library), h->funcname); } if(addr == NULL) { return -1; } int ret = -1; // check if this is a valid hook type if(type >= 0 && type < ARRAYSIZE(hook_types)) { // determine whether we're running under win7, if so, we might have to // follow a short relative jmp and an indirect jump before reaching // the real address OSVERSIONINFO os_info = {sizeof(OSVERSIONINFO)}; if(GetVersionEx(&os_info) && os_info.dwMajorVersion == 6 && os_info.dwMinorVersion == 1) { // windows 7 has a DLL called kernelbase.dll which basically acts // as a layer between the program and kernel32 (and related?) it // allows easy hotpatching of a set of functions which is why // there's a short relative jump and an indirect jump. we want to // resolve the address of the real function, so we follow these // two jumps. if(!memcmp(addr, "\xeb\x05", 2) && !memcmp(addr + 7, "\xff\x25", 2)) { addr = **(unsigned char ***)(addr + 9); } // the following applies for "inlined" functions on windows 7, // some functions are inlined into kernelbase.dll, rather than //
kernelbase.dll jumping kernel32.dll. for these // functions there is a short relative jump, followed by the // inlined function. if(!memcmp(addr, "\xeb\x02", 2) && !memcmp(addr - 5, "\xcc\xcc\xcc\xcc\xcc", 5)) { // step over the short jump and the relative offset addr += 4; } } DWORD old_protect; // make the address writable if(VirtualProtect(addr, hook_types[type].len, PAGE_EXECUTE_READWRITE, &old_protect)) { if(hook_create_trampoline(addr, hook_types[type].len, h->tramp)) { hook_store_exception_info(h); uint8_t special = 0; if(h->allow_hook_recursion == 1) { special = 1; } hook_create_pre_tramp(h, special); // insert the hook (jump from the api to the // pre-trampoline) ret = hook_types[type].hook(h, addr, h->pre_tramp); // if successful, assign the trampoline address to *old_func if(ret == 0) { *h->old_func = h->tramp; // successful hook is successful h->is_hooked = 1; } } // restore the old protection VirtualProtect(addr, hook_types[type].len, old_protect, &old_protect); } } return ret; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53int hook_api ( hook_t * h , int type )
{
// table with all possible hooking types
static struct {
int ( * hook ) ( hook_t * h , unsigned char * from , unsigned char * to ) ;
int len ;
} hook_types [ ] = {
/* HOOK_JMP_DIRECT */ { & hook_api_jmp_direct , 5 } ,
/* HOOK_NOP_JMP_DIRECT */ { & hook_api_nop_jmp_direct , 6 } ,
/* HOOK_HOTPATCH_JMP_DIRECT */ { & hook_api_hotpatch_jmp_direct , 7 } , /* HOOK_PUSH_RETN */ { & hook_api_push_retn , 6 } ,
/* HOOK_NOP_PUSH_RETN */ { & hook_api_nop_push_retn , 7 } ,
/* HOOK_JMP_INDIRECT */ { & hook_api_jmp_indirect , 6 } ,
/* HOOK_MOV_EAX_JMP_EAX */ { & hook_api_mov_eax_jmp_eax , 7 } ,
/* HOOK_MOV_EAX_PUSH_RETN */ { & hook_api_mov_eax_push_retn , 7 } ,
/* HOOK_MOV_EAX_INDIRECT_JMP_EAX */
{ & hook_api_mov_eax_indirect_jmp_eax , 7 } ,
/* HOOK_MOV_EAX_INDIRECT_PUSH_RETN */
{ & hook_api_mov_eax_indirect_push_retn , 7 } ,
#if HOOK_ENABLE_FPU
/* HOOK_PUSH_FPU_RETN */ { & hook_api_push_fpu_retn , 11 } ,
#endif
/* HOOK_SPECIAL_JMP */ { & hook_api_special_jmp , 7 } ,
} ;
// is this address already hooked?
if ( h -> is_hooked != 0 ) {
return 0 ;
}
// resolve the address to hook
unsigned char * addr = h -> addr ;
if ( addr == NULL && h -> library != NULL && h -> funcname != NULL ) {
addr = ( unsigned char * ) GetProcAddress ( GetModuleHandleW ( h -> library ) , h -> funcname ) ;
}
if ( addr == NULL ) {
return - 1 ;
}
int ret = - 1 ;
// check if this is a valid hook type
if ( type >= 0 && type < ARRAYSIZE ( hook_types ) ) {
// determine whether we're running under win7, if so, we might have to
// follow a short relative jmp and an indirect jump before reaching
// the real address
OSVERSIONINFO os_info = { sizeof ( OSVERSIONINFO ) } ;
if ( GetVersionEx ( & os_info ) && os_info . dwMajorVersion == 6 &&
os_info . dwMinorVersion == 1 ) {
// windows 7 has a DLL called kernelbase.dll which basically acts
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论