在今年三月份,下面的样本引起了我的兴趣——因为是通过DGA(域名生成算法)来和C&C服务器进行通信:
例如,在4月12号,生成的前10个域名如下:
nctqrgta7o52.net khqzwlib0pun.org lun0lmr4xe7k.com qvc9inkxy3o9.com z8daz8ti3wxy.net k1ar8levktun.net 1ij05qzgta70.org 6bwdyvw5if45.com jwde7stqb0da.org 05azc52rs1yb.org
这些是什么?
Virustotal扫面了malwr analysis上的样本,除了” Qihoo-360”其他杀软均显示样本正常。360的检测结果显示的是“HEUR/QVM07.1.0000.Malware.Gen”
恶意软件的特征码都是通过和10个字节的XOR密钥(FC 57 91 BC 75 9A 12 CC A4 26)加密的。可以在这里看到完整的文本字符串,如下:
klpszVersion
gBitness
kdwTimestamp dData fLength flpData@
hmainType.gsubType
这些字符串和银行木马Qadars很相似,可以在这篇安全报告中看出。
Qadars二进制文件包含一些硬编码的版本字符串。在我的例子中,版本字符串为3.0.0.0:
在Qadars2.0.0.0的安全智能报告中没有提及域名生成算法,而这个特征将在Qadars3.0.0.0版本中体现。
反汇编DGA下面的列表是DGA算法的反汇编代码。这里我们直接先跳到下一节中,查看基本的DGA属性。
text:004095F0 ; BOOL __cdecl dga(void *pDomain, size_t sld_len)
.text:004095F0 dga proc near ; CODE XREF: sub_409FD0+20_x0019_p
.text:004095F0
.text:004095F0 charset = byte ptr -38h
.text:004095F0 tlds = dword ptr -10h
.text:004095F0 pDomain = dword ptr 8
.text:004095F0 sld_len = dword ptr 0Ch
.text:004095F0
.text:004095F0 push ebp
.text:004095F1 mov ebp, esp
.text:004095F3 sub esp, 38h
.text:004095F6 push ebx
.text:004095F7 push esi
.text:004095F8 mov [ebp+tlds], offset a_com ; ".com"
.text:004095FF mov [ebp+tlds+4], offset a_org ; ".org"
.text:00409606 mov [ebp+tlds+8], offset a_net ; ".net"
.text:0040960D push edi
.text:0040960E mov edi, edi
.text:00409610
.text:00409610 loc_409610: ; CODE XREF: dga+166_x0019_j
.text:00409610 mov ebx, [ebp+pDomain]
.text:00409613 mov ecx, 9
.text:00409618 mov esi, offset charset ; "abcdefghijklmnopqrstuvwxyz0123456789"
.text:0040961D lea edi, [ebp+charset]
.text:00409620 rep movsd
.text:00409622 movsb
.text:00409623 test ebx, ebx
.text:00409625 jz loc_409740
.text:0040962B mov edi, [ebp+sld_len]
.text:0040962E cmp edi, 5
.text:00409631 jbe loc_409740
.text:00409637 cmp domain_nr, 0
.text:0040963E jnz short loc_40966D
.text:00409640 push 0
.text:00409642 call ds:_time64
.text:00409648 add esp, 4
.text:0040964B push 0
.text:0040964D mov esi, eax
.text:0040964F sub eax, 345600
.text:00409654 push 604800
.text:00409659 sbb edx, 0
.text:0040965C push edx
.text:0040965D push eax
.text:0040965E call _allrem
.text:00409663 sub esi, eax
.text:00409665 and esi, 7FFFFFFFh
.text:0040966B jmp short loc_409673
.text:0040966D ; ---------------------------------------------------------------------------
.text:0040966D
.text:0040966D loc_40966D: ; CODE XREF: dga+4Ej
.text:0040966D mov esi, r
.text:00409673
.text:00409673 loc_409673: ; CODE XREF: dga+7Bj
.text:00409673 push edi ; size_t
.text:00409674 push 0 ; int
.text:00409676 push ebx ; void *
.text:00409677 call memset
.text:0040967C lea eax, [ebp+charset]
.text:0040967F add esp, 0Ch
.text:00409682 lea edx, [eax+1]
.text:00409685
.text:00409685 loc_409685: ; CODE XREF: dga+9A_x0019_j
.text:00409685 mov cl, [eax]
.text:00409687 inc eax
.text:00409688 test cl, cl
.text:0040968A jnz short loc_409685
.text:0040968C sub eax, edx
.text:0040968E xor ecx, ecx
.text:00409690 add edi, 0FFFFFFFBh
.text:00409693 mov ebx, eax
.text:00409695 jz short loc_4096CB
.text:00409697 jmp short loc_4096A0
.text:00409697 ; ---------------------------------------------------------------------------
.text:00409699 align 10h
.text:004096A0
.text:004096A0 loc_4096A0: ; CODE XREF: dga+A7j
.text:004096A0 ; dga+D9_x0019_j
.text:004096A0 imul esi, 3E39B193h
.text:004096A6 mov edx, 89F5h
.text:004096AB sub edx, esi
.text:004096AD and edx, 7FFFFFFFh
.text:004096B3 mov esi, edx
.text:004096B5 xor edx, edx
.text:004096B7 mov eax, esi
.text:004096B9 div ebx
.text:004096BB inc ecx
.text:004096BC mov al, [ebp+edx+charset]
.text:004096C0 mov edx, [ebp+pDomain]
.text:004096C3 mov [ecx+edx-1], al
.text:004096C7 cmp ecx, edi
.text:004096C9 jb short loc_4096A0
.text:004096CB
.text:004096CB loc_4096CB: ; CODE XREF: dga+A5j
.text:004096CB imul esi, 3E39B193h
.text:004096D1 mov ecx, 89F5h
.text:004096D6 sub ecx, esi
.text:004096D8 and ecx, 7FFFFFFFh
.text:004096DE mov eax, 55555556h
.text:004096E3 imul ecx
.text:004096E5 mov eax, edx
.text:004096E7 shr eax, 1Fh
.text:004096EA add eax, edx
.text:004096EC lea eax, [eax+eax*2]
.text:004096EF mov r, ecx
.text:004096F5 sub ecx, eax
.text:004096F7 mov ecx, [ebp+ecx*4+tlds]
.text:004096FB mov eax, ecx
.text:004096FD lea ecx, [ecx+0]
.text:00409700
.text:00409700 loc_409700: ; CODE XREF: dga+115_x0019_j
.text:00409700 mov dl, [ecx]
.text:00409702 inc ecx
.text:00409703 test dl, dl
.text:00409705 jnz short loc_409700
.text:00409707 mov edi, [ebp+pDomain]
.text:0040970A sub ecx, eax
.text:0040970C mov edx, ecx
.text:0040970E dec edi
.text:0040970F nop
.text:00409710
.text:00409710 loc_409710: ; CODE XREF: dga+126_x0019_j
.text:00409710 mov cl, [edi+1]
.text:00409713 inc edi
.text:00409714 test cl, cl
.text:00409716 jnz short loc_409710
.text:00409718 mov ecx, edx
.text:0040971A shr ecx, 2
.text:0040971D mov esi, eax
.text:0040971F mov eax, domain_nr
.text:00409724 rep movsd
.text:00409726 mov ecx, edx
.text:00409728 and ecx, 3
.text:0040972B rep movsb
.text:0040972D inc eax
.text:0040972E xor edx, edx
.text:00409730 mov ecx, 0C8h
.text:00409735 div ecx
.text:00409737 mov ebx, [ebp+pDomain]
.text:0040973A mov domain_nr, edx
.text:00409740
.text:00409740 loc_409740: ; CODE XREF: dga+35j
.text:00409740 ; dga+41j
.text:00409740 push ebx
.text:00409741 call gethostbyname ; ws2_32.gethostbyname
.text:00409747 neg eax
.text:00409749 sbb eax, eax
.text:0040974B neg eax
.text:0040974D jnz short loc_40976B
.text:0040974F cmp domain_nr, 0
.text:00409756 jnz loc_409610
.text:0040975C mov edx, [ebp+sld_len]
.text:0040975F push edx ; size_t
.text:00409760 push 0 ; int
.text:00409762 push ebx ; void *
.text:00409763 call memset
.text:00409768 add esp, 0Ch
.text:0040976B
.text:0040976B loc_40976B: ; CODE XREF: dga+15Dj
.text:0040976B xor eax, eax
.text:0040976D cmp domain_nr, eax
.text:00409773 pop edi
.text:00409774 pop esi
.text:00409775 setnz al
.text:00409778 pop ebx
.text:00409779 mov esp, ebp
.text:0040977B pop ebp
.text:0040977C retn
.text:0040977C dga endp
.text:0040977C
.text:0040977C ; ---------------------------------------------------------------------------
在测试gethostbyname的时候,Qadars的域名生成算法已经产生了200多个不同的域名,如果这200个域名都没有办法得到解析,那么Qadars将会sleep 20s(这个不在之前反汇编代码中),接着从第一个域名开始解析。
这个DGA使用线性同余实现而不是通过随机数算法生成。而这个乘法的增加也是不常见的:
r←(35317?1043968403?r)mod2147483647
随机数生成的初始值是系统当前日期:
text:00409640 push 0 .text:00409642 call ds:_time64 .text:00409648 add esp, 4 .text:0040964B push 0 .text:0040964D mov esi, eax .text:0040964F sub eax, 345600 .text:00409654 push 604800 .text:00409659 sbb edx, 0 .text:0040965C push edx .text:0040965D push eax .text:0040965E call _allrem .text:00409663 sub esi, eax .text:00409665 and esi, 7FFFFFFFh
上述反汇编代码算法:
r=(u?(u?4?24?3600))mod7?24?3600
U是当前unix时间戳。分配如下:
计算机的结果在每周四晚上的深夜的值都是不一样的,因为1970年1.1日式星期四。所有的Aadars V3将会生成相同的域名,因为随机数生成的起始值都是一样的。
DGA算法使用硬编码的顶级域名:.com,.org以及.net。第二级别的域名包括12个随机字符串,字符串是从小写字母和数字当中选取。
总结,DGA算法的特性如下:
DGA算法重实现
下面的代码以python的形式显示Qadar的DGA。你也可以在其他地方找到这个算法,例如在我的GitHub上。
import argparse import time from datetime import datetime import time import string def rand(r): return (35317 - 1043968403*r) & 0x7FFFFFFF def dga(date): charset = string.ascii_lowercase + string.digits tlds = [".com", ".org", ".net"] unix = int(time.mktime(date.timetuple())) b = 7*24*3600 c = 3*24*3600 r = ( (unix//b)*b - c) & 0x7FFFFFFF for i in range(200): domain = "" for _ in range(12): r = rand(r) domain += charset[r % len(charset)] r = rand(r) tld = tlds[r % 3] domain += tld print(domain) if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("-d", "--date", help="date for which to generate domains") args = parser.parse_args() if args.date: d = datetime.strptime(args.date, "%Y-%m-%d") else: d = datetime.now() dga(d)