アセンブリからXORデコードの処理を読み取る

Practical Malware Analysis Lab 11-2 のサンプル・バイナリ Lab11-02.dllのXORデコーディングの処理内容について調べました。

本の中の解説ではXORデコーディングの部分については、XORデコードの関数のアドレスにブレークポイントを設定してデコードされたデータを確認するというもので、
XORデコードの処理の内容については触れられていなかったので、アセンブリから処理の内容を読み取ろうと思います。

以下がXORデコードの処理に関するコードです。(一部、関数名をわかりやすい名前に変更しています。)


.text:10001097 ; =============== S U B R O U T I N E =======================================
.text:10001097
.text:10001097 ; Attributes: bp-based frame
.text:10001097
.text:10001097 xor_decode      proc near               ; CODE XREF: decoder+32↓p
.text:10001097
.text:10001097 arg_0           = byte ptr  8
.text:10001097 arg_4           = dword ptr  0Ch
.text:10001097
.text:10001097                 push    ebp
.text:10001098                 mov     ebp, esp
.text:1000109A                 mov     eax, [ebp+arg_4]
.text:1000109D                 and     eax, 0FFh
.text:100010A2                 imul    eax, 29Ah
.text:100010A8                 sar     eax, 4
.text:100010AB                 movsx   ecx, [ebp+arg_0]
.text:100010AF                 xor     eax, ecx
.text:100010B1                 pop     ebp
.text:100010B2                 retn
.text:100010B2 xor_decode      endp
.text:100010B2
.text:100010B3
.text:100010B3 ; =============== S U B R O U T I N E =======================================
.text:100010B3
.text:100010B3 ; Attributes: bp-based frame
.text:100010B3
.text:100010B3 decoder         proc near               ; CODE XREF: sub_10001610+BA↓p
.text:100010B3
.text:100010B3 var_4           = dword ptr -4
.text:100010B3 arg_0           = dword ptr  8
.text:100010B3
.text:100010B3                 push    ebp
.text:100010B4                 mov     ebp, esp
.text:100010B6                 push    ecx
.text:100010B7                 mov     eax, [ebp+arg_0]
.text:100010BA                 mov     [ebp+var_4], eax
.text:100010BD
.text:100010BD loc_100010BD:                           ; CODE XREF: decoder+48↓j
.text:100010BD                 mov     ecx, [ebp+arg_0]
.text:100010C0                 movsx   edx, byte ptr [ecx]
.text:100010C3                 test    edx, edx
.text:100010C5                 jz      short loc_100010FD
.text:100010C7                 mov     eax, [ebp+arg_0]
.text:100010CA                 movsx   ecx, byte ptr [eax]
.text:100010CD                 cmp     ecx, 0Dh
.text:100010D0                 jz      short loc_100010FD
.text:100010D2                 mov     edx, [ebp+arg_0]
.text:100010D5                 movsx   eax, byte ptr [edx]
.text:100010D8                 cmp     eax, 0Ah
.text:100010DB                 jz      short loc_100010FD
.text:100010DD                 push    32h
.text:100010DF                 mov     ecx, [ebp+arg_0]
.text:100010E2                 mov     dl, [ecx]
.text:100010E4                 push    edx
.text:100010E5                 call    xor_decode
.text:100010EA                 add     esp, 8
.text:100010ED                 mov     ecx, [ebp+arg_0]
.text:100010F0                 mov     [ecx], al
.text:100010F2                 mov     edx, [ebp+arg_0]
.text:100010F5                 add     edx, 1
.text:100010F8                 mov     [ebp+arg_0], edx
.text:100010FB                 jmp     short loc_100010BD
.text:100010FD ; ---------------------------------------------------------------------------
.text:100010FD
.text:100010FD loc_100010FD:                           ; CODE XREF: decoder+12↑j
.text:100010FD                                         ; decoder+1D↑j ...
.text:100010FD                 mov     eax, [ebp+var_4]
.text:10001100                 mov     esp, ebp
.text:10001102                 pop     ebp
.text:10001103                 retn
.text:10001103 decoder         endp


decoder関数はループの中で、xor_decode関数を実行します。xor_decode関数は、XORエンコードされたデータ (0x43484d4d58614c404d56405344404f404d58524852434e4e4a0f424e4c) 1バイトずつ と0x32という値を引数として受け取ります。


.text:100010DD                 push    32h
.text:100010DF                 mov     ecx, [ebp+arg_0]
.text:100010E2                 mov     dl, [ecx]
.text:100010E4                 push    edx
.text:100010E5                 call    xor_decode


xor_decode関数の中では以下の処理が行われます。

and     eax, 0FFh

0x32と0x00ffのAND演算を行う。オール1ビットとのAND演算なので0x32の値は変化しない。

imul    eax, 29Ah

0x32と0x029aの掛け算を行う。結果はeaxに格納

sar     eax, 4

eaxの値を右へ4シフト


movsx   ecx, [ebp+arg_0]
xor     eax, ecx


eaxの値とecxの値 (arg_0)のXOR演算を行う。

上記の内容をPythonで表すと以下のようになる。

>>> (((0x32 & 0x00ff) * 0x029a) >> 4) ^ 0x43
2146
>>> bin(2146).replace('0b', '')
'100001100010'

さて、ここで処理がxor_decode関数からdecoder関数に戻ります。


call    xor_decode
add     esp, 8
mov     ecx, [ebp+arg_0]
mov     [ecx], al


xor_decode関数の演算結果から下位8ビット (100001100010) がecxが指すアドレスにコピーされます。(mov [ecx], al)
この下位8ビットがXORデコードされたデータになります。

Pythonで表すと以下のようになります。

下位8ビットの値
>>> bin(2146).replace('0b', '')[-8:]
'01100010'

下位8ビットを10進数に変換
>>> int(bin(2146).replace('0b', '')[-8:], 2)
98

98をASCIIデコードするとアルファベットの'b'になります。
>>> chr(int(bin(2146).replace('0b', '')[-8:], 2))
'b'

これらのXORデコーディングの処理をPythonで書くと以下のようになります。


#!/usr/bin/env python

import binascii

enc = '43484d4d58614c404d56405344404f404d58524852434e4e4a0f424e4c'
dec = ''
cnt = len(enc)
n = 2

for i in range(0, cnt, 2):
    j = (((0x32 & 0x00ff) * 0x029a) >> 4) ^ ord(binascii.unhexlify(enc[i:i+n]))
    j = bin(j).replace('0b', '')[-8:]
    dec += chr(int(j, 2))

print(dec)


上記スクリプトの実行結果。

$ python Lab11-02-decoder.py
billy@malwareanalysisbook.com

以上。

Leave a Reply

Your email address will not be published. Required fields are marked *