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

11장-IDAPython, IDA Pro 스크립팅

by laoching 2015. 2. 18.
728x90
반응형

IDA Pro는 오랫동안 리버스 엔지니어들에 의해 선택된 디스어셈블러다.

IDAPython 설치

구글에 idapython을 검색하면 쉽게 설치가 가능하다.

압축해제하고 plugins 디렉토리 안에 python.plw라는 파일을

ida pro 의 plugins 디렉토리에 복사한다.

그다음 그냥 ida를 실행시켜보면 된다.



IDAPython 함수

IDAPyto은 IDC와 완벽히 호환된다.

IDC에서 지원하는 함수들을 모두 사용할 수 있다는 말이다.


유틸리티 함수

ScreenEA() IDA 화면에 있는 커서의 현재 위치를 구한다.

GetInputFileMD5() IDA에 로드된 바이너리의 MD5 해시 값을 구한다.


세그먼트

IDA에서 바이너리는 여러 세그먼트로 나뉜다.

바이너리 내부의 세그먼트에 관한 정보를 구하는 데 사용되는 함수들

FirstSeg() 바이너리의 첫 번째 세그먼트의 시작 주소를 반환한다.

NextSeg() 바이너리의 다음 세그먼트 시작 주소를 반환한다.

   더 이상 반환할 주소가 없으면 BADADDR 을 반환한다.

SegByName(string SegmentName) 특정 세그먼트 이름을 갖는 세그먼트의 시작 주소를 반환한다. 예를 들어 이 함수를 .text 파라미터와 함께 호출하면 바이너리의 코드 세그먼트 시작 주소가 봔환될 것이다.

SegEnd(long Address) 특정 주소가 포함되는 세그먼트의 마지막 주소를 반환한다.

SegStart(long Address) 특정 주소가 포함되는 세그먼트의 시작 주소를 반환한다.

SegName(long Address) 특정 주소가 포함되는 세그먼트의 이름을 반환한다.

Segments() 바이너리에 있는 모든 세그먼트의 시작 주소 리스트를 반환한다.


함수

스크립트를 작성하다 보면 바이너리 내의 모든 함수를 찾거나 함수의 범위를 판단하는 작업을 자주한다. 그 작업에 유용한 함수들을 모아놨다.

Functions(long StartAddress, long EndAddress) StartAddress와 EndAddress 사이에 존재하는 함수들의 시작 주소 리스트를 반환한다.

Chunks(long FunctionAddress) 함수 리스트를 반환한다. 리스트의 각 아이템은 각 chunk의 시작과 끝점을 포함한다.

LocByName(string FunctionName) 특정 이름을 갖는 함수의 주소를 반환한다.

GetFuncOffset(long AddresS) 함수 내의 주소를 함수 이름과 해당 함수 내에서의 오프셋 값을 나타내는 문자열로 변환하낟.

GetFunctionName(long Address) 특정 주소가 속하는 함수의 이름을 반환한다.


교차 참조

대상 바이너리 내의 흥미있는 지점으로 코드가 어떻게 실행돼 가는지, 그떄의 데이터 흐름이 어떻게 변하는지 판단하려면 코드나 데이터에 대한 교차참조를 찾아내는것이 매우 유용하다.

CodeRefsTo(long Address, bool Flow) 특정 주소에 대한 코드 참조 리스트를 반환한다.

불린 Flow 플래그는 교차 참조를 판단할 때 일반적인 코드 흐름을 따를 것인지 여부를 IDAPython에 전달한다.

CodeRefsFrom(long Address, bool Flow) 특정 주소로부터의 코드 참조 리스트를 반환한다.

DataRefsTo(long Address) 특정 주소에 대한 데이터 참조 리스트를 반환한다. 바이너리 내의 전역 변수 사용을 추적할 때 용이하게 사용할 수 있다.

DataRefsFrom(long Address) 특정 주소로부터의 데이터 참조 리스트를 반환한다.



디버거 후킹

IDAPython이 지원하는 특징 중 하나가 디버거 후킹을 정의하고 다양한 디버깅 이벤트에 대한 이벤트 핸들러를 설정할 수 있다는 점이다.

이 클래스에는 IDA에서의 간단한 디버깅 스크립트를 작성할 때 사용할 수 있는 기본적인 디버그 이벤트를 몇 개 포함한다.

디버거 후킹을 설치하려면

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

debugger = DbgHook()

debugger.hook()

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

이라는 코드를 사용해야 한다.

디버거를 실행시키면 설치한 후킹 핸들러에 디버그 이벤트가 전달된다.

디버깅중에 유용하게 사용할 수 있는 함수들

AddBpt(long Address) 특정 주소에 소프트 프레이크 포인트를 설정한다.

GetBptQty() 현재 설정돼 있는 브레이크포인트의 수를 반환한다.

GetRegValue(string Register) 특정 레지스터의 값을 반환한다.

SetRegValue(long Value, string Register) 특정 레지스터의 값을 설정한다.


스크립트 예제

이제 바이너리에 대한 리버싱 작업을 수행할 때 흔히 수행하는 작업을 쉽고 빠르게 처리할 수 있도록 도와주는 스크립트들을 작성해보자.


위험한 함수에 대한 교차 참조 찾기

cross_ret.py

먼저 위험한 함수들에 대한 주소를 구하고 그 주소가 바이너리 내에서 유요한 주소인지 확인한다.

그 다음에는 위험한 함수를 호출하는 모든 코드 리스트를 구한다.

그 다음 루프를 돌면서 위험한 함수를 호출하는 부분의 주소를 출력하고

IDA 화면상에서 눈에 띄게 해당 명령을 붉은 색으로 표시한다.



함수 코드 커버리지

바이너리를 동적 분석할 때 해당 바이너리가 실행되면서 실제로 바이너리의 어느 코드 부분이 실행되는지 파악할 수 있다면 해당 바이너리 분석에 상당한 도움이 될것이다.

func_coverage.py를 만들자

먼저 디버거 후킹을 설정해 디버거 이벤트가 발생할 때마다 호출되게 만든다.

그리고 모든 함수의 주소를 찾아 그곳에 브레이크포인트를 설정한다.

SetBptAttr 를 이용해 브레이크포인트가 발생할 때 비거거가 멈추지 않게 설정한다.

설정한 브레이크포인트의 총 개수를 출력한다.

브레이크포인트 핸들러는 ea 변수를 이용해 브레이크 포인트가 발생한 주소를 출력한다.

ea 변수는 브레이크포인트가 발생한 시점의 EIP 레지스터의 값을 나타낸다.


스택의 크기 계산

바이너리의 잠재적인 보안 취약점을 찾아내려 할 때 특정 함수 호출에 대한 스택의 크기를 아는 것은 상당히 중요핟.

stack_calc.py를 만들자.

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

from idaapi import *

var_size_threshold   = 16
current_address      = ScreenEA()

for function in Functions(SegStart(current_address), SegEnd(current_address) ):

    stack_frame   = GetFrame( function )

    frame_counter = 0
    prev_count    = -1

    frame_size    = GetStrucSize( stack_frame )

    while frame_counter < frame_size:

        stack_var = GetMemberName( stack_frame, frame_counter )

        if stack_var != "":

            if prev_count != -1:

                distance = frame_counter - prev_distance

                if distance >= var_size_threshold:
                    print "[*] Function: %s -> Stack Variable: %s (%d bytes)" % ( GetFunctionName(function), prev_member, distance )

            else:

                prev_count    = frame_counter
                prev_member   = stack_var

                try:
                    frame_counter = frame_counter + GetMemberSize(stack_frame, frame_counter)
                except:
                    frame_counter += 1
        else:
            frame_counter += 1
-----------------------------------------------------------------------------

먼저 스택 변수가 버퍼일 것이라고 판단하기 위해 임계값을 16으로 설정한다.

전체 함수의 개수만큼 루프를 돌면서

각 함수의 스택 프레임 객체를 구한다.

스택 프레임 객체와 GetStrucSize를 이용해 스택 프레임의 크기를 판단한다.

스택 프레임의 각 바이트를 일일이 조사해 스택 변수가 존재하는지 판단한다.

스택 변수가 존재한다면 현재 바이트 오프셋에서 이전 스택 변수의 오프셋을 뺀다.

이와 같이 두 변수 간의 거리를 계산함으로써 스택 변수의 크기를 계산할 수 있다.

계산한 스택 변수의 크기만큼 현재의 바이트 오프셋 값을 증가시킨다.



728x90
반응형

댓글