idapython
by St1tchIDAPython은 사용자가 다양한 기능에 접근할 수 있게 해주는 플러그인이다.
[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 |
디버거 후킹을 설치하려면 다음의 코드를 사용해야 한다.
1 2 | debugger = DbgHook() debugger.hook() |
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!" |
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 |
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 |
3번 라인. 스택 변수의 버퍼 여부 판단을 위한 임계값 설정.
6번 라인. 전체 함수의 개수만큼 루프 돌입
7번 라인. 각 함수의 스택 프레임 객체 획득
12번 라인. 스택 프레임 객체와 GetStrucSize를 이용해 스택 프레임 크기 판단
15번 라인. 스택 프레임의 각 바이트를 조사, 스택 변수 존재 여부 판단
18번 라인. 스택 변수 존재 시 현재 바이트 오프셋에서 이전 스택 변수의 오프셋을 감소
25번 라인. 계산한 스택 변수의 크기만큼 현재의 바이트 오프셋 값 증가
출처 : 네이버 블로그 어디서 보았는데 메모를 못함..
블로그의 정보
튜기's blogg(st1tch)
St1tch