04 진보된 DLL 디스어셈블러 만들기
원문 : http://kkamagui.springnote.com/pages/389711
들어가기 전에...
요전에 DLL 디스어셈블리어 프로그램을 하나 만들었다. 그런데 단순한 디스어셈블러였을 뿐, 실제 분석에 필요한 뭔가가 없었다. 보통 분석할 때(내가.. ㅡ_ㅡ;;; 다른 사람은 모르겠음...) 함수 단위로 call, jmp 하는 부분 위주로 따라가면서 분석하는데, 그러한 기능이 없었다.
그래서 이러한 함수 분석 기능을 추가하여 JMP, JL, JE 등등과 같은 JMP와 call을 분석해서 따라갈 수 있는 것은 따라가 주도록 만들었다. 아직은 파이썬에 익숙하지 않아서 시간이 좀 걸렸지만... 하면서도 나름 재미있었다는.. ㅠ_ㅠ...
아래는 코드이다.
- # -*- coding: cp949 -*-
from distorm import Decode, Decode16Bits, Decode32Bits, Decode64Bits
# c관련 함수를 사용하기위해 처리
from ctypes import *
import ctypes
- g_assemblyBuffer = [];
g_gotoBufferArray = [];
- # Disassembly를 수행하는 함수
def Disassembly( intAddress, curLevel, maxLevel ):
buffer = ctypes.c_buffer( 4096 );
libc.memcpy( buffer, intAddress, 4096 );
l = Decode(intAddress, buffer, Decode32Bits)
for i in l:
# 아래와 같은 형식으로 출력된다.
# 0x7c8309e1 8bff MOV EDI, EDI
buffer = "0x%08x %-20s %s" % (i[0], i[3].upper(), i[2])
g_assemblyBuffer.append( buffer );
# 만약 Jmp나 call 같은 명령이면 그 뒤에 오는 주소를 넣는다.
if ( "J" in i[ 2 ] or "CALL" in i[ 2 ] ) and \
( "0x" in i[ 2 ] and "+" not in i[ 2 ] and "-" not in i[ 2 ] and \
'*' not in i[ 2 ] and '/' not in i[ 2 ] ) and \
curLevel < maxLevel:
index = i[ 2 ].find( " " );
stringAddr = i[ 2 ][ index + 1 : ];
g_gotoBufferArray.append( [ curLevel + 1, stringAddr ] );
#print "gotoBufferAdd", buffer;
# 만약 ret거나 JMP 이면 그만 한다.
if ( "RET" in i[ 2 ] ) or "NOP" in i[ 2 ]:
g_assemblyBuffer.append( " " );
return ;
- # Hex값을 Int로 바꾼다.
def HexAddrToIntAddr( address ) :
intAddress = 0;
address = address[ 2 : ];
for i in address :
intAddress = intAddress << 4;
if ord( i ) >= ord( '0' ) and ord( i ) <= ord( '9' ) :
intAddress += int( i );
else :
intAddress += ( ord( i ) - ord( 'a' ) + 10 );
return intAddress;
- # Indirect Addressing을 처리 함수
def GetIndirectValue( address ):
intAddress = 0;
intAddress = HexAddrToIntAddr( address );
#print "Indirect Address[%x]" % intAddress;
buffer = ctypes.c_buffer( 4 );
libc.memcpy( buffer, intAddress, 4 );
#little Endian 이므로 역순으로 들어가있다. 이것을 바꿔준다.
value = 0;
mulValue = 0x00000001;
for i in buffer :
value += ( ord( i ) * mulValue );
mulValue = mulValue << 8;
#print "%x" %ord( i );
- #print "%x" %value;
return value;
# 이미 Disassemby한 Address 인가 확인
def IsAlreadyDisassembly( address ):
for i in g_assemblyBuffer:
if address in i[ : 10 ] :
return 1;
return 0;
- # c 런타임 라이브러리를 가지고 있음
libc = cdll.msvcrt;
- dllName = raw_input( "DLL Name : " );
functionName = raw_input( "Procedure Name : " );
maxLevel = input( "Analysis Level( 0 ~ 20 ) : " );
- # 함수를 찾는다.
dll = windll.LoadLibrary( dllName );
functionAddress = dll.GetProcAddress( dll._handle, functionName );
- # 시작 Address를 넣는다.
g_gotoBufferArray.append( [ 0, "0x%x" %functionAddress ] );
- for i in g_gotoBufferArray :
[ level, stringAddress ] = i;
- if '[' in stringAddress :
intAddress = GetIndirectValue( stringAddress[ 1 : -1 ] );
g_assemblyBuffer.append( "==============Level[%d] %s 0x%x============\n" \
%( level, stringAddress, intAddress ) );
else :
intAddress = HexAddrToIntAddr( stringAddress );
g_assemblyBuffer.append( "==============Level[%d] %s============\n" \
%( level, stringAddress ) );
# 점프하는 주소가 이미 Disassembly 되어있으면 안한다.
if IsAlreadyDisassembly( "0x%x" %intAddress ) == 1 :
g_assemblyBuffer.append( "0x%x Is Already Disassembly\n" %intAddress );
continue;
Disassembly( intAddress, level, maxLevel );
- # 출력하는 부분
file = open( dllName + functionName + ".txt", "w" );
print "Now Writing....."
for i in g_assemblyBuffer:
file.writelines( i + "\n" );
#print i;
file.close();
print "Write Complete....."
이것을 실행하면 DLL 이름과 함수 이름, 그리고 얼마나 깊이 call을 추적할 것인가가 나오는데, call 추적은 3~4 정도가 충분하다.
아래는 kernel32.dll, OpenProcess, 3으로 함수를 분석한 결과이다. 아주 깔끔하게 잘 분석되었다 @0@)/~!!
==============Level[0] 0x7c8309e1============
0x7c8309e1 8BFF MOV EDI, EDI
0x7c8309e3 55 PUSH EBP
0x7c8309e4 8BEC MOV EBP, ESP
0x7c8309e6 83EC 20 SUB ESP, 0x20
0x7c8309e9 8B45 10 MOV EAX, [EBP+0x10]
0x7c8309ec 8945 F8 MOV [EBP-0x8], EAX
0x7c8309ef 8B45 0C MOV EAX, [EBP+0xc]
0x7c8309f2 56 PUSH ESI
0x7c8309f3 33F6 XOR ESI, ESI
0x7c8309f5 F7D8 NEG EAX
0x7c8309f7 1BC0 SBB EAX, EAX
0x7c8309f9 83E0 02 AND EAX, 0x2
0x7c8309fc 8945 EC MOV [EBP-0x14], EAX
0x7c8309ff 8D45 F8 LEA EAX, [EBP-0x8]
0x7c830a02 50 PUSH EAX
0x7c830a03 8D45 E0 LEA EAX, [EBP-0x20]
0x7c830a06 50 PUSH EAX
0x7c830a07 FF75 08 PUSH DWORD [EBP+0x8]
0x7c830a0a 8D45 10 LEA EAX, [EBP+0x10]
0x7c830a0d 50 PUSH EAX
0x7c830a0e 8975 FC MOV [EBP-0x4], ESI
0x7c830a11 C745 E0 18000000 MOV DWORD [EBP-0x20], 0x18
0x7c830a18 8975 E4 MOV [EBP-0x1c], ESI
0x7c830a1b 8975 E8 MOV [EBP-0x18], ESI
0x7c830a1e 8975 F0 MOV [EBP-0x10], ESI
0x7c830a21 8975 F4 MOV [EBP-0xc], ESI
0x7c830a24 FF15 0C11807C CALL [0x7c80110c]
0x7c830a2a 3BC6 CMP EAX, ESI
0x7c830a2c 5E POP ESI
0x7c830a2d 0F8C B7710000 JL 0x7c837bea
0x7c830a33 8B45 10 MOV EAX, [EBP+0x10]
0x7c830a36 C9 LEAVE
0x7c830a37 C2 0C00 RET 0xc
==============Level[1] [0x7c80110c] 0x7c93dd7b============
0x7c93dd7b B8 7A000000 MOV EAX, 0x7a
0x7c93dd80 BA 0003FE7F MOV EDX, 0x7ffe0300
0x7c93dd85 FF12 CALL [EDX]
0x7c93dd87 C2 1000 RET 0x10
==============Level[1] 0x7c837bea============
0x7c837bea 50 PUSH EAX
0x7c837beb E8 7B17FDFF CALL 0x7c80936b
0x7c837bf0 33C0 XOR EAX, EAX
0x7c837bf2 E9 3F8EFFFF JMP 0x7c830a36
0x7c837bf7 90 NOP
==============Level[2] 0x7c80936b============
0x7c80936b 8BFF MOV EDI, EDI
0x7c80936d 55 PUSH EBP
0x7c80936e 8BEC MOV EBP, ESP
0x7c809370 56 PUSH ESI
0x7c809371 FF75 08 PUSH DWORD [EBP+0x8]
0x7c809374 FF15 6C10807C CALL [0x7c80106c]
0x7c80937a 8BF0 MOV ESI, EAX
0x7c80937c 56 PUSH ESI
0x7c80937d E8 2EFFFFFF CALL 0x7c8092b0
0x7c809382 8BC6 MOV EAX, ESI
0x7c809384 5E POP ESI
0x7c809385 5D POP EBP
0x7c809386 C2 0400 RET 0x4
==============Level[2] 0x7c830a36============
0x7c830a36 Is Already Disassembly
==============Level[3] [0x7c80106c] 0x7c93fb3d============
0x7c93fb3d 6A 08 PUSH 0x8
0x7c93fb3f 68 78FB937C PUSH 0x7c93fb78
0x7c93fb44 E8 79F2FFFF CALL 0x7c93edc2
0x7c93fb49 64 A1 18000000 MOV EAX, FS:[0x18]
0x7c93fb4f 85C0 TEST EAX, EAX
0x7c93fb51 74 11 JZ 0x7c93fb64
0x7c93fb53 8365 FC 00 AND DWORD [EBP-0x4], 0x0
0x7c93fb57 8B4D 08 MOV ECX, [EBP+0x8]
0x7c93fb5a 8988 F40B0000 MOV [EAX+0xbf4], ECX
0x7c93fb60 834D FC FF OR DWORD [EBP-0x4], -0x1
0x7c93fb64 FF75 08 PUSH DWORD [EBP+0x8]
0x7c93fb67 E8 1D000000 CALL 0x7c93fb89
0x7c93fb6c E8 91F2FFFF CALL 0x7c93ee02
0x7c93fb71 C2 0400 RET 0x4
==============Level[3] 0x7c8092b0============
0x7c8092b0 8BFF MOV EDI, EDI
0x7c8092b2 55 PUSH EBP
0x7c8092b3 8BEC MOV EBP, ESP
0x7c8092b5 56 PUSH ESI
0x7c8092b6 57 PUSH EDI
0x7c8092b7 64 A1 18000000 MOV EAX, FS:[0x18]
0x7c8092bd 8B75 08 MOV ESI, [EBP+0x8]
0x7c8092c0 8BF8 MOV EDI, EAX
0x7c8092c2 A1 C446887C MOV EAX, [0x7c8846c4]
0x7c8092c7 85C0 TEST EAX, EAX
0x7c8092c9 0F85 20090300 JNZ 0x7c839bef
0x7c8092cf 3977 34 CMP [EDI+0x34], ESI
0x7c8092d2 0F85 43040000 JNZ 0x7c80971b
0x7c8092d8 5F POP EDI
0x7c8092d9 5E POP ESI
0x7c8092da 5D POP EBP
0x7c8092db C2 0400 RET 0x4
역시나 오늘도 파이썬 만쉐 @0@)/~!!
이 글은 스프링노트에서 작성되었습니다.