| 
 | 
 
 
我在 malwareresearchgroup.slack.com 上发现一个样本,为 CS 加载器,样本哈希值为 2569cc660d2ae0102aa74c98d78bb9409ded24101a0eeec15af29d59917265f3,2021-09-01 19:47:50 被人上传到 VT 上,其中有 37 家检测引擎把它标记为恶意,如下所示: 
 
 
 
一、分析加载器 
 
这个加载器是 64 位的 Dll 文件,MinGW 编译,有一个导出函数: 
 
 
 
在 IDA 下,ServiceMain 函数将生成一个新线程 (我将其重命名为 f_spawn_shellcode_thread),如下所示: 
 
 
 
f_spawn_shellcode_thread 函数干以下几件事: 
 
- [1] 初始化 xor_key 的值为 jKfXmEkWYshKkZdPhJYS;
 - [2] 分配用于存储加密 ShellCode 的堆缓冲区,值为最开始声明的的全局字节数组;
 - [3] 进入解密 ShellCode 循环;
 - [4] 在新线程中解密 ShellCode;
 
 
  
 
 
我编写了一简短脚本来提取 ShellCode,以供后面分析: 
 
[pre] 
import sys 
import pefile 
  
xor_key = "jKfXmEkWYshKkZdPhJYS" 
  
def decode_sc(data, key): 
    key_len = len(key) 
    data_len = len(data) 
    decrypted = bytearray(data_len)  
  
    for i in range(0, data_len): 
        decrypted = data ^ key[i%key_len] 
          
    print("Decode Done!") 
    return decrypted 
  
def extract_sc(input_file): 
    encrypted_sc = [] 
    try: 
        print("\r\nFile: " + input_file) 
        pe = pefile.PE(input_file) 
  
        for section in pe.sections: 
            if b'.rdata\x00\x00' in section.Name: 
                rdata_section = bytearray(section.get_data()) 
          
        size = 0        
        for i in rdata_section: 
            if rdata_section[size] == 0x00 and rdata_section[size+1] == 0x00: 
                break 
            else: 
                size += 1 
        print("Encrypted bytes size: " + str(size - 24) + " bytes") 
  
        encrypted_bytes = rdata_section[24:size+1] 
        for i in range(len(encrypted_bytes)): 
            if ((i & 1) == 0): 
                encrypted_sc.append(encrypted_bytes) 
  
        key = xor_key.encode('ascii') 
        decrypted_sc = decode_sc(encrypted_sc, key) 
  
        with open(sys.argv[1]+"-decrypted", "wb") as out_file: 
            out_file.write(decrypted_sc) 
        print("Shellcode extracted at " + sys.argv[1]+"-decrypted!\r\n")         
  
        print("Extract Shellcode Done!") 
    except Exception as e: 
        print("Error: " + str(e)) 
  
if __name__ == '__main__': 
    if len(sys.argv) == 2: 
        extract_sc(sys.argv[1]) 
    else: 
        print("Usage: cobalt_extract_sc.py <cobalt_loader_dll>") 
[/pre] 
跑完脚本后,得到的 ShellCode 如下所示: 
 
 
 
二、分析 ShellCode 
 
如果我们将原始 ShellCode 加载到 IDA 中并转换为 asm 代码,它将如下图所示。在这段代码的第一个开头,我们可以看到定位 PEB 结构的代码。这让我想到它会使用 PEB 最终来查找 API 的地址。 
 
 
 
进入 sub_D2,第一条语句将返回地址分配给 rbp 寄存器。我们知道这个地址是 0xA (push r9),然后我们看到字符串值 wininet 被加载到 0xD5 处的 r14 寄存器。我们看到一个值被分配给 r10 (726774Ch; 726774Ch) 寄存器,接下来是对 rbp 寄存器指向的地址的调用。我认为这些哈希值是与 api 函数相关,ShellCode 会进行计算并与这些值进行比较,从中获取相关的 API 地址。 
 
 
 
为了便于分析和调试,我把 ShellCode 转成 exe,最后得到以下与通过 jmp rax 命令查找 API 函数地址和调用 API 相关的伪代码: 
 
 
 
根据上面的伪代码,我们可以看到 ShellCode 会计算两个哈希值,第一个值基于 Dll 的名称,第二个值基于该 Dll 的 API 函数的名称,将这两个值加在一起并与预先计算的哈希值进行比较。 
 
你可以编写脚本来恢复 API 函数,我总是使用 FLARE Team 的 shellcode_hashes_search_plugin.py。 详细信息可以在 这篇文章 中找到。 使用插件后的最终结果: 
 
[pre] 
shellcode_hash: Starting up 
[INFO] Starting up  (shellcode_hash_search:run) 
shellcode_hash: Processing current segment only: 0x140001000 - 0x140003000 
[INFO] Processing current segment only: 0x140001000 - 0x140003000   (shellcode_hash_search:processCode) 
shellcode_hash: 0x1400020e7: ror13AddHash32AddDll:0x0726774c kernel32.dll!LoadLibraryA 
[INFO] 0x1400020e7: ror13AddHash32AddDll:0x0726774c kernel32.dll!LoadLibraryA   (shellcode_hash_search:lookForOpArgs) 
shellcode_hash: 0x1400020ff: ror13AddHash32AddDll:0xa779563a wininet.dll!InternetOpenA 
[INFO] 0x1400020ff: ror13AddHash32AddDll:0xa779563a wininet.dll!InternetOpenA   (shellcode_hash_search:lookForOpArgs) 
shellcode_hash: 0x140002121: ror13AddHash32AddDll:0xc69f8957 wininet.dll!InternetConnectA 
[INFO] 0x140002121: ror13AddHash32AddDll:0xc69f8957 wininet.dll!InternetConnectA    (shellcode_hash_search:lookForOpArgs) 
shellcode_hash: 0x140002140: ror13AddHash32AddDll:0x3b2e55eb wininet.dll!HttpOpenRequestA 
[INFO] 0x140002140: ror13AddHash32AddDll:0x3b2e55eb wininet.dll!HttpOpenRequestA    (shellcode_hash_search:lookForOpArgs) 
shellcode_hash: 0x14000216a: ror13AddHash32AddDll:0x869e4675 wininet.dll!InternetSetOptionA 
[INFO] 0x14000216a: ror13AddHash32AddDll:0x869e4675 wininet.dll!InternetSetOptionA  (shellcode_hash_search:lookForOpArgs) 
shellcode_hash: 0x140002184: ror13AddHash32AddDll:0x7b18062d wininet.dll!HttpSendRequestA 
[INFO] 0x140002184: ror13AddHash32AddDll:0x7b18062d wininet.dll!HttpSendRequestA    (shellcode_hash_search:lookForOpArgs) 
shellcode_hash: 0x140002329: ror13AddHash32AddDll:0x56a2b5f0 kernel32.dll!ExitProcess 
[INFO] 0x140002329: ror13AddHash32AddDll:0x56a2b5f0 kernel32.dll!ExitProcess    (shellcode_hash_search:lookForOpArgs) 
shellcode_hash: 0x140002345: ror13AddHash32AddDll:0xe553a458 kernel32.dll!VirtualAlloc 
[INFO] 0x140002345: ror13AddHash32AddDll:0xe553a458 kernel32.dll!VirtualAlloc   (shellcode_hash_search:lookForOpArgs) 
shellcode_hash: 0x140002363: ror13AddHash32AddDll:0xe2899612 wininet.dll!InternetReadFile 
[INFO] 0x140002363: ror13AddHash32AddDll:0xe2899612 wininet.dll!InternetReadFile    (shellcode_hash_search:lookForOpArgs) 
shellcode_hash: Done 
[INFO] Done (shellcode_hash_search:run) 
[/pre] 
 
 
此时可以进行调试以进行进一步的分析,但是,我很快使用 hasherezade 的 tiny_tracer 工具来跟踪 ShellCode: 
 
 
 
 
 
样本解压密码: 
 
 
 |   
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册 
 
 
 
x
 
 
 
 
 |