天堂之門

CPQ_1發表於2024-04-23

天堂之門

WoW64是Windows x64提供的一種相容機制,可以認為WoW64是64位Windows系統建立的一個32位的模擬環境,使得32位可執行程式能夠在64位的作業系統上正常執行

所以也弄明白了之前為什麼32位的dll需要放在WoW64裡面了,而64位的dll需要放在System32裡面

image-20240423122920623

系統執行程式的時候,會檢測CS段暫存器的值來呼叫API

CS為0x33的時候會切換到64位模式,當CS為0x23的時候就會切換到32位模式

下面透過天堂之門技術呼叫WIN32 API的過程。這裡我們透過一些操作繞過了WoW64機制,手動切換到64位模式並呼叫64位下的ZwOpenProcess函式,大致流程如下(和圖中不太一樣):

  1. 將cs段暫存器設為0x33,切換到64位模式
  2. 從gs:0x60讀取64位PEB
  3. 從64位PEB中定位64位ntdll基址
  4. 遍歷ntdll64匯出表,讀取ZwOpenProcess函式地址
  5. 構造64位函式呼叫

結合最近的做題,我總結就是找到更改cs段暫存器值的地方,然後根據彙編找到跳轉的對應地址

關注0X33和0x23和關鍵的彙編指令 jmp far ptr.....

dump下來可以用bn看,然後就可以逆向解密,最近發現了ida的一個外掛

OllyDumpEx · Issue #41 · mentebinaria/retoolkit (github.com)

也是可以直接在ida裡面直接dump了,對於除錯可以使用windbg或者CE(目前對於這兩個的使用還差的很多,還要學)

unicorn

Unicorn 是一個輕量級, 多平臺, 多架構的 CPU 模擬器框架. 我們可以更好地關注 CPU 操作, 忽略機器裝置的差異. 想象一下, 我們可以將其應用於這些情景: 比如我們單純只是需要模擬程式碼的執行而非需要一個真的 CPU 去完成那些操作, 又或者想要更安全地分析惡意程式碼, 檢測病毒特徵, 或者想要在逆向過程中驗證某些程式碼的含義. 使用 CPU 模擬器可以很好地幫助我們提供便捷.

Programming with C & Python languages – Unicorn – The Ultimate CPU emulator (unicorn-engine.org)

它的亮點 (這也歸功於 Unicorn 是基於 qemu 而開發的) 有:

  • 支援多種架構: Arm, Arm64 (Armv8), M68K, Mips, Sparc, & X86 (include X86_64).
  • 對 Windows 和 nix 系統 (已確認包含 Mac OSX, Linux, BSD & Solaris) 的原生支援
  • 具有平臺獨立且簡潔易於使用的 API
  • 使用 JIT 編譯技術, 效能表現優異

官方文件裡講了c和python介面,這裡主要說一下python介面

 1 from __future__ import print_function
 2 from unicorn import *
 3 from unicorn.x86_const import *
 4 
 5 # code to be emulated
 6 X86_CODE32 = b"\x41\x4a" # INC ecx; DEC edx
 7 
 8 # memory address where emulation starts
 9 ADDRESS = 0x1000000
10 
11 print("Emulate i386 code")
12 try:
13     # Initialize emulator in X86-32bit mode
14     mu = Uc(UC_ARCH_X86, UC_MODE_32)
15 
16     # map 2MB memory for this emulation
17     mu.mem_map(ADDRESS, 2 * 1024 * 1024)
18 
19     # write machine code to be emulated to memory
20     mu.mem_write(ADDRESS, X86_CODE32)
21 
22     # initialize machine registers
23     mu.reg_write(UC_X86_REG_ECX, 0x1234)
24     mu.reg_write(UC_X86_REG_EDX, 0x7890)
25 
26     # emulate code in infinite time & unlimited instructions
27     mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32))
28 
29     # now print out some registers
30     print("Emulation done. Below is the CPU context")
31 
32     r_ecx = mu.reg_read(UC_X86_REG_ECX)
33     r_edx = mu.reg_read(UC_X86_REG_EDX)
34     print(">>> ECX = 0x%x" %r_ecx)
35     print(">>> EDX = 0x%x" %r_edx)
36 
37 except UcError as e:
38     print("ERROR: %s" % e)
  • 行號 2~3: 在使用 Unicorn 前匯入unicorn模組. 樣例中使用了一些 x86 暫存器常量, 所以也需要匯入unicorn.x86_const模組
  • 行號 6: 這是我們需要模擬的二進位制機器碼, 使用十六進位制表示, 代表的彙編指令是: "INC ecx" 和 "DEC edx".
  • 行號 9: 我們將模擬執行上述指令的所在虛擬地址
  • 行號 14: 使用Uc類初始化 Unicorn, 該類接受 2 個引數: 硬體架構和硬體位數 (模式). 在樣例中我們需要模擬執行 x86 架構的 32 位程式碼, 我 們使用變數mu來接受返回值.
  • 行號 17: 使用mem_map方法根據在行號 9 處宣告的地址, 對映 2MB 用於模擬執行的記憶體空間. 所有程序中的 CPU 操作都應該只訪問該記憶體區域. 對映的記憶體具有預設的讀, 寫和執行許可權.
  • 行號 20: 將需要模擬執行的程式碼寫入我們剛剛對映的記憶體中. mem_write方法接受 2 個引數: 要寫入的記憶體地址和需要寫入記憶體的程式碼.
  • 行號 23~24: 使用reg_write方法設定ECX和EDX暫存器的值
  • 行號 27: 使用emu_start方法開始模擬執行, 該 API 接受 4 個引數: 要模擬執行的程式碼地址, 模擬執行停止的記憶體地址 (這裡是 X86_CODE32的最後 1 位元組處), 模擬執行的時間和需要執行的指令數目. 如果我們像樣例一樣忽略後兩個引數, Unicorn 將會預設以無窮時間和無窮指令數目的條件來模擬執行程式碼.
  • 行號 32~35: 列印輸出ECX和EDX暫存器的值. 我們使用函式reg_read來讀取暫存器的值.

後面對於這個的使用會補上一些題....

每日一題

image-20240423124321508

最開始點進來發現是一串數字,c了過後感覺也不太對,覺得是SMC,然後動態走一下,小改一下花指令

image-20240423124530028

基本可以確定是SMC了,然後除錯開始亂飛

從這裡開始飛,然後根據邏輯判斷這裡也是關鍵的加密部分image-20240423124753744

image-20240423133939148

jmp far ptr可以知道的是可以同時改變eip和cs段暫存器值的指令

這種格式的jmp指令實現的是上述的段間轉移,同時修改CS和IP,在跳轉範圍大於-32768~32767時使用jmp near ptr不會編譯失敗,但是會連結失敗。
該指令執行後CS:IP將同時修改

這個不要看反彙編指令,其實是跳轉的是4011D0這個地址,修改的CS段暫存器為0X33

然後可以在bn裡設定64位然後檢視反彙編,有個反除錯PEB.BeingDebugged

記得繞過

dump下來後可以看見

image-20240423172644654

image-20240423172806348

image-20240423172815371

最後求flag的過程不難,就是簡單的異或和迴圈左移右移

貼一個其他師傅寫的指令碼

rol = lambda val, r_bits, max_bits: \
    (val << r_bits%max_bits) & (2**max_bits-1) | \
    ((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits)))
ror = lambda val, r_bits, max_bits: \
    ((val & (2**max_bits-1)) >> r_bits%max_bits) | \
    (val << (max_bits-(r_bits%max_bits)) & (2**max_bits-1))
flag = b'1234adc89012345678901234a27890ab'
cmp = [0xAA, 0x4F, 0x0F, 0xE2, 0xE4, 0x41, 0x99, 0x54, 0x2C, 0x2B, 0x84, 0x7E, 0xBC, 0x8F, 0x8B, 0x78, 0xD3, 0x73, 0x88, 0x5E, 0xAE, 0x47, 0x85, 0x70, 0x31, 0xB3, 0x09, 0xCE, 0x13, 0xF5, 0x0D, 0xCA]

# key = [0x9d,0x44,0x37,0xb5]
key = [4,0x77,0x82, 0x4a]
print(bytes(cmp))
print()
def enc(flag):
    enc = b''
    num = 0x3CA7259D
    for i in range(8):
        cur = int.from_bytes(flag[4*i:4*i+4],'little')
        cur = (cur+num)&0xffffffff
        num ^= cur
        enc += cur.to_bytes(4,'little')
    print("enc1 ", enc)
    enc = bytearray(enc )

    cur = int.from_bytes(enc[0:8],'little')
    enc[0:8] = rol(cur,0xc, 64).to_bytes(8,'little')
    cur = int.from_bytes(enc[8:16],'little')
    enc[8:16] = rol(cur,0x22, 64).to_bytes(8,'little')
    cur = int.from_bytes(enc[16:24],'little')
    enc[16:24] = rol(cur,0x38, 64).to_bytes(8,'little')
    cur = int.from_bytes(enc[24:32],'little')
    enc[24:32] = rol(cur,0xe, 64).to_bytes(8,'little')
    # for i in range(4):
    #     enc[i*8:i*8+4], enc[i*8+4:i*8+8] = enc[i*8+4:i*8+8], enc[i*8:i*8+4],
    print("enc2 ", bytes(enc))
    
    for i in range(32):
        enc[i] ^= key[i%4]
    print("enc3 ", enc)

    
    return enc 

def dec(cmp):
    print("dec3 ", cmp)
    dec = [0]*32
    cmp = bytearray(cmp)
    
    for i in range(32):
        cmp[i] ^= key[i%4]
    
    print("dec2 ", cmp)
    # for i in range(4):
    #     cmp[i*8:i*8+4], cmp[i*8+4:i*8+8] = cmp[i*8+4:i*8+8], cmp[i*8:i*8+4]
    cur = int.from_bytes(cmp[0:8],'little')
    cmp[0:8] = ror(cur,0xc, 64).to_bytes(8,'little')
    cur = int.from_bytes(cmp[8:16],'little')
    cmp[8:16] = ror(cur,0x22, 64).to_bytes(8,'little')
    cur = int.from_bytes(cmp[16:24],'little')
    cmp[16:24] = ror(cur,0x38, 64).to_bytes(8,'little')
    cur = int.from_bytes(cmp[24:32],'little')
    cmp[24:32] = ror(cur,0xe, 64).to_bytes(8,'little')
    print("dec1 ", bytes(cmp))
    
    num = 0x3CA7259D
    for i in range(8):
        num ^= int.from_bytes(cmp[4*i:4*i+4],'little')
    for i in range(7,-1,-1):
        cur = int.from_bytes(cmp[4*i:4*i+4],'little')
        num ^= cur
        cur = (cur+0x100000000-num)&0xffffffff
        dec[4*i:4*i+4] = cur.to_bytes(4,'little')
    return bytes(dec)


assert(dec(enc(flag)) == flag)



print('----------------------')
print(dec(cmp))  # 6cc1e44811647d38a15017e389b3f704

相關文章