1. SAMPLE

Play ransomware

2. Static analysis

  • Hàm main:

image

  • Có vẻ IDA dịch không rõ lắm, xem lại asm:

image

image

  • Có vẻ hàm lấy giá trị trên cùng của stack + 0x35

  • Note:

Khi chương trình gọi lệnh call một hàm thì giá trị trên cùng của stack sẽ lưu địa chỉ của instruction tiếp theo.

-> Có nghĩa lấy địa chỉ của pop edi0xD042F1 + 0x35.

  • Debug thử:

image

image

-> Đúng là như vậy.

  • Sau khi tính toán được giá trị mới thì chương trình nhảy đến địa chỉ đó luôn:

image

-> Tới đây ta có thể patch lại bằng cách tự tính toán địa chỉ và thay thể lệnh call bằng jump.

image

  • Có thể dùng tool keypatch để patch lại cho dễ.

  • Kéo tiếp xuống dưới lại gặp hàm tương tự nên ta cũng thực hiện tương tự cuối cùng có thể có thể decompile được hàm main:

image

3. Patch anti-analysis

  • Đi sâu vào hàm main ta bắt gặp kĩ thuật tương tự và rất nhiều nên không thể patch lại bằng tay toàn bộ được.

  • Để ý các các chỗ obfuscate đó đều có dạng như sau:

image

image

-> Có thể dựa vào để tìm và patch lại các chỗ bị obfuscate

import re
from pwn import *
data= open("PLAY_ransom.exe", "rb").read()

regex = b'\x06\x81\xC4....\x83\xC4.\xE8....'
count=0

for match in re.finditer(regex, data):
    count += 1
    next_ins_addr = match.start()+15    # ins addr after call ins
    bytes_matched= match.group()
    func_called_addr=u32(bytes_matched[bytes_matched.find(b'\xe8')+1:])+next_ins_addr
    
    add_val=data[func_called_addr+3]

    #jmp_addr=next_ins_addr+add_val
    patch_addr=next_ins_addr-5

    #patch
    data1=list(data)
    data1[patch_addr:patch_addr+5]=list(b'\xe9'+p8(add_val)+b'\x00'*3) #change call =jmp
    data=bytes(data1)

pat=open("patched.exe", "wb")
pat.write(data)
print("[+]Done")

4. API hashing

  • Quan sát sub_40C750:

image

  • Ta thấy có rất nhiều hàm như vậy và mỗi hàm đều có hằng số đưa vào, dựa trên kinh nghiệm mấy bài trước khá chắc đây là kĩ thuật API hashing

  • Xem hàm thử:

image

  • Chương trình lấy API_name rồi tính hash rồi so sánh với giá trị ban đầu đưa vào.

  • Hàm calculate_hash sử dụng bằng phép tính toán gì đó rất dài nên không thể nào viết lại bằng python rồi tính hash được.

-> Debug cũng không được vì chỉ có thể debug được đối với binary ban đầu còn binary sau khi patch không biết sao lại lỗi:

image

-> Nhưng khi debug đối với cái ban đầu thì không đơn giản là đặp bp tại đó rồi debug vì khi đó chương trình sẽ nhảy vào code rác của obfuscate và gây crash chương trình -> bắt buộc phải đặt bp tại main rồi step từ từ cho chương trình tự deobfuscate tới khi nào đến chỗ cần xem -> rất mất thời gian.

Emulate calculate_hash dùng unicorn

-> Chỉ cần emulate code của hàm calculate_hash rồi đưa vào các tham số phù hợp là được.

  • Extract code của hàm calculate_hash:
import idaapi, idc
func_addr = idc.get_name_ea_simple("calculate_hash")
func = idaapi.get_func(func_addr)
func_OPCODE = idaapi.get_bytes(func.start_ea, func.size())
print(func_OPCODE)

-> Lưu ý bỏ byte \xc3 (ret) ở cuối để unicorn chạy được

-> Ta tạo một list lưu các API để brute dựa trên giá trị hash API_list.txt

import json

json_data=json.loads(open("API_list.txt", "rb").read())
API_names=json_data['exports']

hash_vals=[3196191668, 587706791, 430125299, 745928694, 3310323133, 2538134804, 2160950796, 3321973177, 2119526180, 2016564646, 819148513, 2706107229, 2274961481, 4009797727, 2329007555, 1515022974, 4070557793, 3184347727, 3230171373, 1150531511, 2458654477, 1274182115, 2607238143, 1002302808, 2229935675, 3774275279, 2276756918, 3884594833, 2318578073, 1560409820, 3301358813, 922136913, 2092703519, 2554527287, 2432991358, 3496982552, 373201559, 1243518349, 4110329759, 3017027465, 65123382, 1513489892, 1081677820, 711108549, 2797954528, 199922510, 1914963876, 1145734755, 1493453468, 791102906, 3656038948, 1967849689, 57836566, 1700271275, 2941172387, 1965304112, 3919218989, 470098108, 333447649, 1590973787, 1777329460, 2419093438, 2261379902, 1660342600, 1434042011, 2363481290, 2480380205, 804062357, 2152398498, 2541341025, 1101559834, 689679891, 1248996702, 2088540052, 1497197006, 335155875, 969247798, 3560560371, 851486428, 2185952081, 429295890]


from unicorn import *
from unicorn.x86_const import *
import struct
def EMULATE(inBuff:bytes) -> Uc:
    # ------------------Initialization ------------------
    # remove "retn" instruction from SHA1Hash function opcodes or -> UC_ERR_FETCH_UNMAPPED -> no ret address on stack
    CALCULATE_HASH_OPCODE = b"U\x8b\xec\x83\xec V\x8b\xc2\x89M\xfc3\xf6\x89E\xe0W\x8b\xf8\x85\xc9u\x1b\x8bM\x08\x81\xc1\xb1gV\x16\x8b\xc1\xc1\xe8\x0f3\xc1i\xc8w\xca\xeb\x85\xe9\x07\x02\x00\x00S\x83\xf8\x10\x0f\x82j\x01\x00\x00\x8b]\x08\x8d\x93(D#$\x89U\xe8\x8d\x83O\x86\xc8a\x8d\x93w\xca\xeb\x85\x89U\xec\x8dQ\x03\x89U\xf8\x8dQ\x02A\x89U\xf4\x8bU\xf8\x89M\xf0\x8b\xcf\xc1\xe9\x04\x89M\xe4\x0f\xb6\x142\x83\xef\x10\x8bM\xf4\xc1\xe2\x08\x0f\xb6\x0c1\x0b\xd1\x8bM\xf0\xc1\xe2\x08\x0f\xb6\x0c1\x0b\xd1\x8bM\xfc\xc1\xe2\x08\x0f\xb6\x0c\x0e\x0b\xd1i\xca\x895\x14z\x8bU\xe8+\xd1\x8bM\xf8\xc1\xc2\ri\xd2\xb1y7\x9e\x89U\xe8\x0f\xb6T1\x04\x8bM\xf4\xc1\xe2\x08\x0f\xb6L1\x04\x0b\xd1\x8bM\xf0\xc1\xe2\x08\x0f\xb6L1\x04\x0b\xd1\x8bM\xfc\xc1\xe2\x08\x0f\xb6L\x0e\x04\x0b\xd1i\xca\x895\x14z\x8bU\xec+\xd1\x8bM\xf8\xc1\xc2\ri\xd2\xb1y7\x9e\x89U\xec\x0f\xb6T1\x08\x8bM\xf4\xc1\xe2\x08\x0f\xb6L1\x08\x0b\xd1\x8bM\xf0\xc1\xe2\x08\x0f\xb6L1\x08\x0b\xd1\x8bM\xfc\xc1\xe2\x08\x0f\xb6L\x0e\x08\x0b\xd1i\xca\x895\x14z+\xd9\x8bM\xf8\xc1\xc3\ri\xdb\xb1y7\x9e\x0f\xb6T1\x0c\x8bM\xf4\xc1\xe2\x08\x0f\xb6L1\x0c\x0b\xd1\x8bM\xf0\xc1\xe2\x08\x0f\xb6L1\x0c\x0b\xd1\x8bM\xfc\xc1\xe2\x08\x0f\xb6L\x0e\x0c\x83\xc6\x10\x0b\xd1i\xca\x895\x14z\x8bU\xf8+\xc1\xc1\xc0\ri\xc0\xb1y7\x9e\x83m\xe4\x01\x0f\x85\xe8\xfe\xff\xff\x8bU\xec\x8bM\xfc\xc1\xc8\x0e\xc1\xc3\x0c\xc1\xc2\x07\x03\xc3\x03\xc2\x8bU\xe8\xd1\xc2\x03\xc2\xeb\x08\x8bE\x08\x05\xb1gV\x16\x03E\xe0\x83\xff\x04rP\x8b\xdf\xc1\xeb\x02\xeb\x05f\x90\x8bM\xfc\x0f\xb6T1\x03\x83\xef\x04\x0f\xb6L1\x02\xc1\xe2\x08\x0b\xd1\x8bM\xfc\xc1\xe2\x08\x0f\xb6L1\x01\x0b\xd1\x8bM\xfc\xc1\xe2\x08\x0f\xb6\x0c1\x83\xc6\x04\x0b\xd1i\xca\xc3QM=+\xc1\xc1\xc8\x0fi\xc0/\xeb\xd4'\x83\xeb\x01u\xb9[\x85\xfft!\x8bU\xfc\x03\xd6\x0f\xb6\n\x8dR\x01i\xc9\xb1gV\x16\x03\xc8\xc1\xc1\x0bi\xc1\xb1y7\x9e\x83\xef\x01u\xe4\x8b\xc8\xc1\xe9\x0f3\xc8i\xc9w\xca\xeb\x85\x8b\xc1\xc1\xe8\r3\xc1i\xc8=\xae\xb2\xc2_^\x8b\xc1\xc1\xe8\x103\xc1\x8b\xe5]"
    OPCODE_ADDRESS = 0x400000
    API_LEN = len(inBuff)
    CONSTVAL = 1
    BUFFERADDR = OPCODE_ADDRESS + 0x200000
    # ------------------ Setting + Starting Emulator ------------------
    try:
        mu = Uc(UC_ARCH_X86, UC_MODE_32)                                        # set EMU architecture and mode
        mu.mem_map(OPCODE_ADDRESS, 0x200000, UC_PROT_ALL)                       # map memory for SHA1Hash function opcodes, stack etc.
        mu.mem_write(OPCODE_ADDRESS, CALCULATE_HASH_OPCODE)                           # write opcodes to memory
        mu.mem_map(BUFFERADDR, 0x1000, UC_PROT_ALL)                             # map memory for input to be hashed
        mu.mem_write(BUFFERADDR, inBuff)                                        # write input bytes to memory
       
        mu.reg_write(UC_X86_REG_ESP, OPCODE_ADDRESS + 0x100000)                 # initialize stack (ESP)
        mu.reg_write(UC_X86_REG_EBP, OPCODE_ADDRESS + 0x100000)                 # initialize frame pointer (EBP)                                 # set EAX register (argument) -> CONSTVAL
         
        mu.mem_write(OPCODE_ADDRESS + 0x100000+4, struct.pack("<I", 1)) 
        mu.reg_write(UC_X86_REG_EDX, API_LEN)                             
        mu.reg_write(UC_X86_REG_ECX, BUFFERADDR)       

        mu.emu_start(OPCODE_ADDRESS, OPCODE_ADDRESS + len(CALCULATE_HASH_OPCODE))    
        return mu
    except UcError as e:
        print("ERROR: %s" % e)

res={}
for val in hash_vals:    
    for api in API_names:
            
        mu = EMULATE(api.encode())
        if (mu.reg_read(UC_X86_REG_EAX)+0x4E986790)&0xffffffff == val:
            res[val]=api
            break

print(res)
  • Kết quả:
xx={3196191668: 'LoadLibraryA', 587706791: 'VirtualAlloc', 430125299: 'VirtualFree', 745928694: 'FindFirstFileW', 3310323133: 'FindNextFileW', 2538134804: 'FindClose', 2160950796: 'CreateFileA', 3321973177: 'CreateFileW', 2119526180: 'ReadFile', 2016564646: 'WriteFile', 819148513: 'SetFilePointer', 2706107229: 'WaitForMultipleObjects', 2274961481: 'WaitForSingleObject', 4009797727: 'CreateThread', 2329007555: 'GetFileAttributesW', 1515022974: 'GetFileAttributesA', 4070557793: 'GetModuleFileNameA', 3184347727: 'GetCurrentProcess', 3230171373: 'CloseHandle',1150531511: 'GetWindowsDirectoryA', 2458654477: 'Sleep', 1274182115: 'GetFileSizeEx', 2607238143: 'GetDriveTypeW', 1002302808: 'SetFilePointerEx', 2229935675: 'SetFileAttributesW', 3774275279: 'HeapFree', 2276756918: 'GetProcessHeap', 3884594833: 'GetLogicalDriveStringsW', 2318578073: 'GetDiskFreeSpaceExW', 1560409820: 'FindFirstVolumeW', 3301358813: 'GetVolumePathNamesForVolumeNameW', 922136913:'FindNextVolumeW', 2092703519: 'FindVolumeClose', 2554527287: 'SetVolumeMountPointW', 2432991358: 'GetTempPathW', 3496982552: 'MoveFileW', 373201559: 'VirtualProtect', 1243518349: 'K32EnumProcessModules', 4110329759: 'K32EnumProcessModulesEx', 3017027465: 'K32GetModuleFileNameExA', 65123382: 'GetCommandLineW', 1513489892: 'CreateDirectoryW', 1081677820: 'GetTickCount', 711108549: 'GetModuleFileNameW', 2797954528: 'GetFileSize', 199922510: 'GetWindowsDirectoryW', 1914963876: 'CopyFileW', 1145734755: 'RtlInitializeCriticalSection', 1493453468: 'RtlDeleteCriticalSection', 791102906: 'RtlEnterCriticalSection', 3656038948: 'RtlLeaveCriticalSection', 1967849689: 'RtlAllocateHeap', 57836566: 'BCryptOpenAlgorithmProvider', 1700271275: 'BCryptCloseAlgorithmProvider', 2941172387: 'BCryptGenerateSymmetricKey', 1965304112: 'BCryptSetProperty', 3919218989: 'BCryptGenRandom', 470098108: 'BCryptDestroyKey', 333447649: 'BCryptEncrypt', 1590973787: 'BCryptExportKey', 1777329460: 'BCryptImportKeyPair', 2419093438: 'NetShareEnum', 2261379902:'NetApiBufferFree', 1660342600: 'IcmpCreateFile', 1434042011: 'IcmpCloseHandle', 2363481290: 'GetAdaptersInfo', 2480380205: 'IcmpSendEcho2', 804062357: 'InetNtopW', 2152398498: 'inet_ntop', 2541341025: 'WSAStartup', 1101559834: 'WSACleanup', 689679891: 'inet_addr', 1248996702: 'socket', 2088540052: 'htons', 1497197006: 'connect', 335155875: 'closesocket', 969247798: 'CommandLineToArgvW', 3560560371: 'WNetAddConnection2W', 851486428: 'WNetGetUniversalNameW', 2185952081: 'CreateProcessWithLogonW', 429295890: 'GetUserNameW'}

Đổi tên biến

xx={3196191668: 'LoadLibraryA', 587706791: 'VirtualAlloc', 430125299: 'VirtualFree', 745928694: 'FindFirstFileW', 3310323133: 'FindNextFileW', 2538134804: 'FindClose', 2160950796: 'CreateFileA', 3321973177: 'CreateFileW', 2119526180: 'ReadFile', 2016564646: 'WriteFile', 819148513: 'SetFilePointer', 2706107229: 'WaitForMultipleObjects', 2274961481: 'WaitForSingleObject', 4009797727: 'CreateThread', 2329007555: 'GetFileAttributesW', 1515022974: 'GetFileAttributesA', 4070557793: 'GetModuleFileNameA', 3184347727: 'GetCurrentProcess', 3230171373: 'CloseHandle',1150531511: 'GetWindowsDirectoryA', 2458654477: 'Sleep', 1274182115: 'GetFileSizeEx', 2607238143: 'GetDriveTypeW', 1002302808: 'SetFilePointerEx', 2229935675: 'SetFileAttributesW', 3774275279: 'HeapFree', 2276756918: 'GetProcessHeap', 3884594833: 'GetLogicalDriveStringsW', 2318578073: 'GetDiskFreeSpaceExW', 1560409820: 'FindFirstVolumeW', 3301358813: 'GetVolumePathNamesForVolumeNameW', 922136913:'FindNextVolumeW', 2092703519: 'FindVolumeClose', 2554527287: 'SetVolumeMountPointW', 2432991358: 'GetTempPathW', 3496982552: 'MoveFileW', 373201559: 'VirtualProtect', 1243518349: 'K32EnumProcessModules', 4110329759: 'K32EnumProcessModulesEx', 3017027465: 'K32GetModuleFileNameExA', 65123382: 'GetCommandLineW', 1513489892: 'CreateDirectoryW', 1081677820: 'GetTickCount', 711108549: 'GetModuleFileNameW', 2797954528: 'GetFileSize', 199922510: 'GetWindowsDirectoryW', 1914963876: 'CopyFileW', 1145734755: 'RtlInitializeCriticalSection', 1493453468: 'RtlDeleteCriticalSection', 791102906: 'RtlEnterCriticalSection', 3656038948: 'RtlLeaveCriticalSection', 1967849689: 'RtlAllocateHeap', 57836566: 'BCryptOpenAlgorithmProvider', 1700271275: 'BCryptCloseAlgorithmProvider', 2941172387: 'BCryptGenerateSymmetricKey', 1965304112: 'BCryptSetProperty', 3919218989: 'BCryptGenRandom', 470098108: 'BCryptDestroyKey', 333447649: 'BCryptEncrypt', 1590973787: 'BCryptExportKey', 1777329460: 'BCryptImportKeyPair', 2419093438: 'NetShareEnum', 2261379902:'NetApiBufferFree', 1660342600: 'IcmpCreateFile', 1434042011: 'IcmpCloseHandle', 2363481290: 'GetAdaptersInfo', 2480380205: 'IcmpSendEcho2', 804062357: 'InetNtopW', 2152398498: 'inet_ntop', 2541341025: 'WSAStartup', 1101559834: 'WSACleanup', 689679891: 'inet_addr', 1248996702: 'socket', 2088540052: 'htons', 1497197006: 'connect', 335155875: 'closesocket', 969247798: 'CommandLineToArgvW', 3560560371: 'WNetAddConnection2W', 851486428: 'WNetGetUniversalNameW', 2185952081: 'CreateProcessWithLogonW', 429295890: 'GetUserNameW'}

dword_val=[4380404, 4380328, 4380320, 4380312, 4380244, 4380316, 4380380, 4380184, 4380392, 4380240, 4380188, 4380188, 4380256, 4380340, 4380260, 4380204, 4380204, 4380292, 4380232, 4380352, 4380416, 4380360, 4380296, 4380172, 4380248, 4380288, 4380268, 4380280, 4380280, 4380180, 4380284, 4380208, 4380364, 4380384, 4380252, 4380152, 4380160, 4380192, 4380336, 4380336, 4380356, 4380216, 4380412, 4380144, 4380144, 4380144, 4380144, 4380144, 4380156, 4380168, 4380220, 4380344, 4380276, 4380332, 4380264, 4380140, 4380196, 4380212, 4380164, 4380176, 4380348, 4380300, 4380272, 4380372, 4380400, 4380376, 4380148, 4380200, 4380304, 4380420, 4380224, 4380228, 4380388, 4380324, 4380396, 4380408, 4380236, 4380308, 4380368]
xx1=[]

import idc

for i in xx:
    xx1.append(xx[i])

for i in range(len(dword_val)):
    idc.set_name(dword_val[i], xx1[i], idaapi.SN_FORCE)
  • Kết quả:

image

5. Argument options

image

image

image

-mc: chạy bình thường
-d <drive_path>: encrypt drive 
-ip <shared_network> <user> <pass> :encrypt network
-p <path>: encrypt folder

6. Encryption

  • Sử dụng BCryptOpenAlgorithmProvider để tạo một CNG provider:

image

  • Tạo file README.txt ơ thư mục drive và lưu cái chuỗi trên vào file:

image

image

  • Dùng FindFirstFileW., ..để duyệt tìm các file trong thư mục hiện hành và thư mục cha:

image

image

  • Né không encrypt loại file README.txt và các extensions: .PLAY, .exe, .dll, .lnk, .sys, bootmgr, .msi

image

image

  • Chương trình tạo 1 cái thread riêng để thực hiện encryption:

image

image

Data Encryption

  • Tạo một IV random gồm 16 bytes, sau đó export AES key vào một blob có kích thước 0x230 bytes:

image

image

  • Chương trình đọc data từ file rồi tiến hành encrypt data dùng AES key và IV đã tạo sau đó lưu kết quả vào file lại:

image

image

image

Encrypt dựa trên kích thước file

  • Nếu kích thước file >0x500000 bytes: encrypt chunk đầuchunk cuối mỗi chunk 0x100000 bytes
  • Nếu kích thước <= 0x100000 bytes: encrypt toàn bộ data của file
  • Nếu kích thước >0x100000 và <=0x500000 bytes: thực hiện vòng lặp encrypt mỗi lần 0x100000 bytes đến chừng nào hết data

image

image

Đổi extension

  • Sau khi encrypt file, chương trình đổi extension của file thành .PLAY:

image