04 진보된 DLL 디스어셈블러 만들기

원문 :  http://kkamagui.springnote.com/pages/389711

 

들어가기 전에...

 

 요전에 DLL 디스어셈블리어 프로그램을 하나 만들었다. 그런데 단순한 디스어셈블러였을 뿐, 실제 분석에 필요한 뭔가가 없었다. 보통 분석할 때(내가.. ㅡ_ㅡ;;; 다른 사람은 모르겠음...) 함수 단위로 call, jmp 하는 부분 위주로 따라가면서 분석하는데, 그러한 기능이 없었다.

 그래서 이러한 함수 분석 기능을 추가하여 JMP, JL, JE 등등과 같은 JMP와 call을 분석해서 따라갈 수 있는 것은 따라가 주도록 만들었다. 아직은 파이썬에 익숙하지 않아서 시간이 좀 걸렸지만... 하면서도 나름 재미있었다는.. ㅠ_ㅠ...

 아래는 코드이다.

  1. # -*- coding: cp949 -*-
    from distorm import Decode, Decode16Bits, Decode32Bits, Decode64Bits
    # c관련 함수를 사용하기위해 처리
    from ctypes import *
    import ctypes
  2. g_assemblyBuffer = [];
    g_gotoBufferArray = [];
  3. # 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 ;
  4. # 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;
  5. # 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 );
  6.     #print "%x" %value;
        return value;

  7. # 이미 Disassemby한 Address 인가 확인
    def IsAlreadyDisassembly( address ):
        for i in g_assemblyBuffer:
            if address in i[ : 10 ] :
                return 1;
        return 0;
  8. # c 런타임 라이브러리를 가지고 있음
    libc = cdll.msvcrt;
  9. dllName = raw_input( "DLL Name : " );
    functionName = raw_input( "Procedure Name : " );
    maxLevel = input( "Analysis Level( 0 ~ 20 ) : " );
  10. # 함수를 찾는다.
    dll = windll.LoadLibrary( dllName );
    functionAddress = dll.GetProcAddress( dll._handle, functionName );
  11. # 시작 Address를 넣는다.
    g_gotoBufferArray.append( [ 0, "0x%x" %functionAddress ] );
  12. for i in g_gotoBufferArray :
        [ level, stringAddress ] = i;
  13.     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 );
  14. # 출력하는 부분
    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@)/~!!

이 글은 스프링노트에서 작성되었습니다.

+ Recent posts