idapython

Reversing/리버싱 이론

2015. 12. 14. 01:19

IDAPython은 사용자가 다양한 기능에 접근할 수 있게 해주는 플러그인이다.

[11.2] IDAPython 함수

     IDAPython은 IDC와 완전 호환된다. 모든 IDC 함수는 IDAPython에서 사용 가능하다.

[11.2.1] 유틸리티 함수

ScreenEA() : IDA 화면에 있는 커서의 현재 위치 획득

GetInputFileMD5() : IDA에 로드된 바이너리의 MD5 해시 값 획득

    바이너리 버전 변경이 내용에 영향을 미치는지 확인할 때 유용

[11.2.2] 세그먼트

※IDA에서 바이너리는 여러 세그먼트(CODE, DATA, BSS, STACK, CONST, XTRN)로 나뉜다.

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

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

SegByName( string SegmentName ) : 특정 세그먼트 이름을 갖는 세그먼트의 시작 주소 반환

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

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

Segment() : 바이너리에 있는 모든 세그먼트의 시작 주소 리스트 반환


[11.2.3] 함수

다음은 바이너리 내의 함수와 관련된 작업 수행 시 유용한 함수들이다.

Functions( long StartAddress, long EndAddress ) : StartAddress와 EndAddress 사이에 존재하는 함수들 시작 주소 반환

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

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

GetFuncOffset( long Address ) : 함수 내의 주소를 함수 이름과 해당 함수 내 오프셋 값을 나타내는 문자열로 변환

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


[11.2.4] 교차 참조

대상 바이너리 내부의 코드 실행 상태, 해당 시점의 데이터 흐름의 관측을 위해서는 교차 참조를 탐색하는 것이 좋음

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

Flow 플래그는 교차 참조 판단 시 일반적 코드 흐름 유지 여부를 결정

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

DataRefsTo( long Address ) : 특정 주소에 대한 데이터 참조 리스트 반환

바이너리 내 전역 변수 사용 추적 시 용이

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


[11.2.5] 디버거 후킹

IDA는 내부에서 디버거 후킹을 정의하고 다양한 디버깅 이벤트에 대한 핸들러 설정이 가능

디버거 후킹 설정을 위해 먼저 디버거 후킹 클래스 설정 후 해당 클래스 내에 다양한 이벤트 핸들러를 정의해야 함

이하의 코드는 디버거 후킹 클래스의 예시이다.

1
2
3
4
5
6
7
8
9
10
11
12
class DbgHook(DBG_Hooks):
    def dbg_process_start(self, pid, tid, ea, name, base, size):
        return
 
    def dbg_process_exit(self, pid, tid, ea, code):
        return
 
    def dbg_library_load(self, pid, tid, ea, name, base, size):
        return
 
    def dbg_bpt(self, tid, ea):
        return

cs



디버거 후킹을 설치하려면 다음의 코드를 사용해야 한다.

1
2
debugger = DbgHook()
debugger.hook()

cs




AddBpt( long Address ) : 특정 주소에 소프트 브레이크포인트 설정

GetBptQty() : 현재 설정된 브레이크포인트 수 반환

GetRegValue( string Register ) : 특정 레지스터 값 반환

SetRegValue( long Value, string Register ) : 특정 레지스터 값 설정


[11.3] 스크립트 예제

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

개발 시 사용하면 좀 곤란해지는 함수들이 존재한다. 문자열 복사 함수나 메모리 복사 함수가 그런 범주에 속한다.

이하의 코드는 상기 범주에 포함되는 함수들을 추적하고 해당 함수들이 호출됐는지 파악하는 스크립트다.

cross_ref.py

1
2
3
4
5
6
7
8
9
10
11
12
13
from idaapi import *
danger_funcs = ["strcpy","sprintf","strncpy"]
for func in danger_funcs:
    addr = LocByName( func )
    if addr != BADADDR:
        cross_refs = CodeRefsTo( addr, 0 )
        print "Cross References to %s" % func
        print "-------------------------------"
        for ref in cross_refs:
            print "%08x" % ref
            SetColor( ref, CIC_ITEM, 0x0000ff)
 
print "End!"

cs





4번 라인. 위험 함수 주소 획득 후 해당 주소의 바이너리 내부 유효성 확인

6번 라인. 위험 함수를 호출하는 모든 코드 리스트 획득

11번 라인. 해당 명령을 IDA 화면 상에서 적색으로 표시

정상 작동.


[11.3.2] 함수 코드 커버리지

func_coverage.py

1
2
3
4
5
6
7
8
9
10
11
12
13
from idaapi import *
class FuncCoverage(DBG_Hooks):
    def dbg_bpt(self, tid, ea):
        print "[*] Hit: 0x%08x" % ea
        return
debugger = FuncCoverage()
debugger.hook()
current_addr = ScreenEA()
for function in Functions(SegStart( current_addr ), SegEnd( current_addr )):
    AddBpt( function )
    SetBptAttr( function, BPTATTR_FLAGS, 0x0 )
num_breakpoints = GetBptQty()
print "[*] Set %d breakpoints." % num_breakpoints

cs



6번 라인. 디버거 후킹 설정, 디버거 이벤트 발생 시 호출

9번 라인. 모든 함수의 주소 획득

10번 라인. 해당 주소에 브레이크포인트 설정

12번 라인. 설정한 브레이크포인트 갯수 획득


정상 작동.


[11.3.3] 스택 크기 계산

stack_calc.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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_count
                
                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

cs




3번 라인. 스택 변수의 버퍼 여부 판단을 위한 임계값 설정.

6번 라인. 전체 함수의 개수만큼 루프 돌입

7번 라인. 각 함수의 스택 프레임 객체 획득

12번 라인. 스택 프레임 객체와 GetStrucSize를 이용해 스택 프레임 크기 판단 

15번 라인. 스택 프레임의 각 바이트를 조사, 스택 변수 존재 여부 판단

18번 라인. 스택 변수 존재 시 현재 바이트 오프셋에서 이전 스택 변수의 오프셋을 감소 

25번 라인. 계산한 스택 변수의 크기만큼 현재의 바이트 오프셋 값 증가


출처 : 네이버 블로그 어디서 보았는데 메모를 못함..