본문 바로가기
파이썬 스터디 과제/파이썬 해킹 프로그래밍

3장-1 디버기

by laoching 2015. 1. 14.
728x90
반응형

디버기

프로세스를 디버깅하려면 어떤 식으로든 먼저 해당 프로세스에 연결해야 한다.

디버깅할 프로세스를 실행시키거나 이미 실행중인 프로세스에 붙여야 한다.

윈도우는 이 두 가지 작업을 쉽게 수행할 수 있게 하는 디버깅 API를 제공한다.


프로세스를 실행하는 것 // 프로세스에 붙히는 것

프로세스를 실행시키는 경우는 해당 프로세스의 코드가 실행되기 전에 제어를 할 수 있다는 장점이 있다. 악성코드를 분석할 때 좋겠다.

프로세스에 붙히는 것은 단지 이미 실행 중인 프로세스에 연결하는 것이다. 관심이 있는 특정 영역의 코드만을 분석할 수 있다.

디버깅하려는 대상 프로세스가 어떤 것인지, 분석하려는 목적이 무엇인지에 따라 두 가지 방법 중 하나를 선택하면 된다.


프로세스를 실행시켜 디버깅하기

디버거가 실행 바이너리를 직접 실행시키는 것이다.

바이너리? : 두 조각, 두 부분을 의미한다. 0,1을 의미한다.

윈도우에서 프로세스를 실행시키려면 'CreateProcessA()'함수를 호출해야 한다. 그리고 함수에 전달하는 특정 플래그의 값을 설정함으로써 프로세스를 디버깅 모드로 실행시킬 수 있다.

특정 플래그의 값이 뭘까? 또 어떻게 설정할까? 삽질을 해봐야 겠다.

CreateProcessA() 함수의 프로토타입

프로토타입? : 원래의형태, 기초 또는 표준을 뜻함

-------------------------------------------------------------

BOOL WIN API CreateProcessA(

LPCSTR lpApplicationName,

LPCSTR lpCommandLine,

LPSECURITY_ATTRIBUTES lpProcessAttributes,

LPSECURITY_ATTRIBUTES lpThreadAttributes.

BOOL bInheritHandles,

DWORD dwCreationFlags,

LPVOID lpEnviroment,

LPCTSTR lpCurrentDirectory,

LPSTARTUPINFO lpStartupInfo,

LPPROCESS_INFORMATION lpProcessInformation

);

-------------------------------------------------------------

딱 보면 겁나 복잡해보인다. 책에서는 디버거가 프로세스를 실행시키기 위해 함수에 전달하는 주요 파라미터만을 살펴보라고 한다.

해당 파라미터는

lpApplicationName, lpCommandLine, dwCreationFlags, lpStartupInfo, lpProcessInformation 이다.

그 외 나머지 파라미터에는 NULL값을 입력해도 된다고 한다. 이 함수에 대한 자세한 설명은 MSDN을 참고하라고 한다.

http://msdn2.microsoft.com/en-us/library/ms682425.aspx <<들어가보니 다 영어다...

파라미터에 대한 해석

처음 두 파라미터는 실행 시킬 실행 바이너리의 경로와 커맨드라인 인자를 전달하기 위해 사용된다.

dwCreationFlags 파라미터에 특별한 값을 전달함으로써 해당 프로세스가 디버깅 목적으로 실행된다는 점을 명시할 수 있다.

그 특별한 값이 뭘까? 이것도 삽질

마지막 두 파라미터는 구조체(STARTUPINFO,PROCESS_INFORMATION)에 대한 포인터로, 해당 구조체에는 프로세스가 어떻게 실행되야 하는지에 대한 정보와 성공적으로 실행된 이후를 위한 정보가 있다. 


my_debugger.py와 my_debugger_defines.py라는 두 개의 새로운 파이썬 파일을 만들자.

그리고 debugger()클래스를 만들자. 이 클래스에 조금씩 기능을 추가할 것이다.

my_debugger_defines.py 파일에는 정의된 모든 상수값과 구조체, 유니언을 위치시킨다.

여기서 짜는 소스는 다 win 32bit에서 해야한다. 

my_debugger_defines.py

-------------------------------------------------------------

#-*- coding: utf-8 -*-
from ctypes import *

# ctypes 형태의 타입을 마이크로소프트의 타입으로 매핑하자.
WORD = c_ushort
DWORD = c_ulong
LPBYTE = POINTER(c_ubyte)
LPTSTR = POINTER(c_char)
HANDLE = c_void_p

#상수
DEBUG_PROCESS = 0x00000001
CREATE_NEW_CONSOLE = 0x00000010

#CreateProcessA() 함수를 위한 구조체
class STARTUPINFO(Structure):
    _fields_=[
        ("cb",                  DWORD),
        ("lpReserved",          LPTSTR),
        ("lpDesktop",           LPTSTR),
        ("lpTItle",                LPTSTR),
        ("dwx",                  DWORD),
        ("dwXSize",             DWORD),
        ("dwYSize",             DWORD),
        ("dwXCountChars",     DWORD),
        ("dwYCountChars",   DWORD),
        ("dwFillAttribute",     DWORD),
        ("dwFlags",             DWORD),
        ("wShowWindow",     WORD),
        ("cbReserved2",         WORD),
        ("lpReserved2",         LPBYTE),
        ("hStdInput",           HANDLE),
        ("hStdOutput",          HANDLE),
        ("hStdError",           HANDLE),
       ]

    class PROCESS_INFORMATION(Structure):
        _fields_=[
            ("hProcess",            HANDLE),
            ("hThread",             HANDLE),
            ("dwProcessId",         DWORD),
            ("dwThread",            DWORD),
            ]
-------------------------------------------------------------

my_debugger.py

-------------------------------------------------------------

#-*- coding: utf-8 -*-

from ctypes import *
from my_debugger_defines import *

kernel32 = windll.kernel32

class debugger():
    def __init__(self):
        pass
   
    def load(self,path_to_exe):

        # dwCreation 플래그를 이용해 프로세스를 어떻게 생성할 것인지 판단한다.
        # 계산기의 GUI를 보고자 한다면 creation_flags를
        # CREATE_NEW_CONSOLE로 설정하면 된다.
        creation_flags=DEBUG_PROCESS

        #구조체 인스턴스화
        startupinfo                 =STARTUPINFO()
        process_information      =PROCESS_INFORMATION()

        #다음의 두 옵션은 프로세스가 독립적인 창으로 실행되게 만들어준다.
        #이는 STARTCUPINFO struct 구조체의 설정 내용에 다라 디버기 프로세스에
        #어떤 영향을 미치는지 보여준다
        startupinfo.dwFlags             =0x1
        startupinfo.wShowWindow    =0x0

        #다음에는 STARTUPINFO struct 구조체 자신의 크기를 나타내는 cb 변수 값을
        #초기화한다.
        startupinfo.cb = sizeof(startupinfo)

        if kernel32.CreateProcessA(path_to_exe,
                                   None,
                                   None,
                                   None,
                                   None,
                                   creation_flags,
                                   None,
                                   None,
                                   byref(startupinfo),
                                   byre(process_information)):
            print "[*] we have successfully launched the process!"
            print "[*] PID : %d" % process_information.dwProcessId

        else:
            print "[*] Error : 0x%08." % kernel32.GetLastError()

-------------------------------------------------------------

my.test.py

-------------------------------------------------------------

import my_debbuer

debugger = my_debugger.debugger()
debugger.load("C:\\WINDOWS\\system32\\calc.exe")

-------------------------------------------------------------

커맨드라인이나 IDE로 위 파이썬 파일은 실행시키면 프로세스 ID(PID)가 출력되면서 해당 프로세스가 실행된다. 즉, 위 코드와 동일하게 calc.exe를 이용했다면 계산기 프로그램의 GUI를 볼 수 없을 것이다.

그 이유는 프로세스가 디버거로부터 실행을 계속하게 하는 명령을 받지 못해 아직 화면에 GUI를 그리지 못했기 때문이다. 


실행 중인 프로세스에 디버거를 붙이기

프로세스에 붙이기 위해서는 먼저 해당 프로세스에 대한 핸들을 구해야 한다.

여기서 사용하는 함수의 대부분이 프로세스 핸들을 필요로 한다.

디버깅하려는 프로세스의 핸들을 먼저 구하면 그 프로세스에 접속이 가능한지 여부를 판단할 수 있다. 프로세스 핸들을 얻기 위해 사용하는 함수는 OpenProcess()다. 이는 kernel32.dll에서 익스포트하는 함수다.

OpenProcess()의 프로토 타입

-------------------------------------------------------------

HANDLE WINAPI OpenProcess(

DWORD dwDesiredAccess,

BOOL bInheritHandle

DWORD dwProcessId

);

-------------------------------------------------------------

dwDesiredAccess 파라미터에는 어떤 종류의 접근 권한을 가진 프로세스 핸들을 원하는지 입력한다. 프로세스에 대한 디버깅을 수행하려면 PROCESS_ALL_ACCESS값으로 설정해야 한다.

bInheritHandle 파라미터는 항상 False값으로 설정하고 dwProcessId 파라미터에는 단순히 핸들을 얻고자 하는 프로세스의 PID를 입력하면 된다.

함수가 성공적으로 수행되면 해당 프로세스 객체에 대한 핸들을 반환할 것이다.

OpenProcess 함수 MSDN : http://msdn2.microsoft.com/ko-kr/library/ms684320.aspx 

역시 영어다.

프로세스에 붙일 때는 DebugActiveProcess() 함수를 이용한다.

-------------------------------------------------------------

BOOL WINAPI DebugActiveProcess(

DWORD dwProcessId

);

-------------------------------------------------------------

단순히 어태치할 프로세스의 PID를 전달하면 된다.

어태치? : 첨부하다.

시스템은 일단 디버거가 프로세스에 대한 올바른 권한을 갖고 있다고 판단하면 디버거가 해당 프로세스의 디버그 이벤트를 처리할 준비가 됐다고 가정한다. 그리고 해당 프로세스에 대한 제어권을 디버거에 넘겨준다.

디버거는 WaitForDebugEvent() 함수를 이용해 디버그 이벤트를 처리한다.

WaitForDebugEvent()의 프로토 타입

-------------------------------------------------------------

BOOL WINAPI WaitForDebugEvent(

LPDEBUG_EVENT lpDebugEvent,

DWORD dwMilliseconds

);

-------------------------------------------------------------

첫 번째 파라미터는 디버그 이벤트를 설명해주는 DEBUG_EVENT 구조체에 대한 포인터다. 두 번째 파라미터를 INFINITE로 설정하면 디버그 이벤트가 발생할 때까지 WaitForDebugEvent() 함수는 리턴하지 않고 대기한다.

발생하는 각 이벤트에 대해서는 그것을 처리하기 위한 이벤트 핸들러가 각기 있으며 이벤트 핸들러는 이벤트에 맞는 어떤 작업을 수행한 후에 프로세스가 실행을 계속하게 해준다.

그래서 일단 이벤트 핸들러의 작업이 완료되면 프로세스가 실행을 계속하게 만들어 줘야 하는데 그것을 위해 사용하는 함수가 ContinueDebugEvent()다.

ContinueDebugEvent() 의 프로토타입

-------------------------------------------------------------

BOOL WINAPI CointinueDebugEvent(

DWORD dwProcessId,

DWORD dwThreadId,

DWORD dwContinueStatus

);

-------------------------------------------------------------

dwProcessId와 dwThreadId 파라미터는 디버그 이벤트가 발생할 때 설정되는 DEBUG_EVENT 구조체의 필드 내용이다. dwContinueStatus 파라미터를 이용해 프로세스가 실행을 계속하게 만들거나 아니면 계속해서 예외를 처리하게 만든다.

남은 작업은 붙였던 프로세스에서 뗴어내는 일이다. PID를 파라미터로 DebugActivePRocessStop() 함수에 전달하면 해당 프로세스에서 떼어낼 수 있다.

728x90
반응형

댓글