소프트웨어 버그 헌팅 - 3 (IDA 스크립트를 활용한 findkey 문제 풀이)
IDA 스크립트를 활용한 findkey 문제 풀이

findkey.exe

이번 포스팅에서는 아이다에서 파이썬 스크립트와 findkey.exe파일을 통해

정적 분석 및 문제 풀이를 해볼 것이다

IDA에서 파이썬 스크립트 환경 구축하는 것은 "소프트웨어 버그 헌팅 2.1" 글에 있으니 확인하면 된다.

아이다에서 findkey.exe를 실행시키고 아래의 내용을 진행한다.


ida code patch 2.zip

ida script.zip

IDA 스크립트는 여러개 존재하며 위의 캡처본으로 확인 가능

 

라인에 커서를 올리고 ea = here() 하면 ea에 해당 라인이 저장된다.

 

print "0x%x" % (ea) // 형식은 C랑 동일

위의 명령어를 입력하면 아까전에 ea에 등록한 곳의 값이 출력된다.


File -> Script Command(Shift+F2)에서 스크립트를 작성한다.

idc.getdisasm(ea) // ea에 주소를 직접 적어줘도 됨
idc.GetMnem(ea)
idc.GetOpnd(ea,0)
for seg in Segments():
    print idc.SegName(seg), hex(idc,SegStart(seg)), hex(idc.SegEnd(seg))

Script Language를 Python으로 바꿔주고 Run 한다.

그럼 아래 커맨드 창에 세그먼트 주소가 나오는데 CTRL + S로 세그먼트 주소를 비교해보고 맞는지 확인한다.

 


ea=here()
func = idaapi.get_func(ea)
print "'Start 0x%x, End: 0x%x" % (func.startEA, func.endEA)
start = func.startEA
end = func.endEA
while start <= end:
    print hex(start), idc.GetDisasm(start)
    start = idc.NextHead(start,end)

 

함수의 시작과 끝을 출력해주는 스크립트

올리 디버거에서 보는 명령창처럼 확인할 수 있음

NextHead가 없으면 한문장만 계속 나옴


* 메인 화면에서 우클릭 텍스트 뷰 <-> 그래프 뷰 변환이 가능함


printf = idc.LocByName("_printf")
print hex(printf)

for addr in CodeRefsTo(printf,0):
    print hex(addr), idc.Getdisasm(addr)

함수 이름으로 함수 주소를 찾는 스크립트이며, 어셈블리 명령어까지 출력하는 소스코드

많이 취약하다고 하는 memcpy 등의 취약 함수를 위의 명령어로 찾아서 어셈블리코드를 가져와

취약한 곳 찾을 때 좀 더 수월하게 찾을 수 있음.

출력하면 주소가 2개 나오는 데 주소 더블 클릭하면 해당 주소로 감

 


xref 상관없이 상호 참조하는 거 다 보여줄 때 쓰는 스크립트

메인화면서에서 커서를 찍어줘야 한다.

ea = here()
for xref in XrefsTo(ea):
 print idc.GetDisasm(xref.to)

 


바이너리 패턴 검색을 위한 스크립트

함수 프롤로그에 있는 PUSH EBP를 찾고싶다.

그래서 55 PUSH를 찾으면 나머지 값도 나오기때문에

MO EBP, ESP를 같이 찾으면 된다(프롤로그에서 같이 나오기 때문에)


pattern = '55 89 E5'
addr = MinEA()
for x in range(0,5):
    addr = idc.FindBinary(addr, SEARCH_DOWN | SEARCH_NEXT, pattern)
    if addr != idc.BADADDR:
        print hex(addr), idc.GetDisasm(addr)
(들여쓰기 주의)

그러면 push EBP가 나오는게 보이고 주소도 보인다. 이렇게 함수를 찾을 수 있다.

앞의 주소를 더블클릭하면 해당주소가 있는 곳으로 넘어간다.

 


문자열을 찾을 수 있다.

"what"이 들어간 문자열을 찾아보자

string = "what"
start = MinEA()
end = MaxEA()

while start < end:
    start = idc.FindText(start, SEARCH_DOWN, 0, 0, string)
    if start == idc.BADADDR:
        break
    else:
        print hex(start), idc.GetDisasm(start)
    start = idc.NextHead(start)

 


색 입히는 스크립트

코드 조사하면서 하이라이팅할 필요가 생긴다.

연속되는 코드나 비슷한 업무를 하기 위한 소스코드를 표기하거나 할때 쓰인다.

SetColor(주소,CIC_ITEM,색코드)

SetColor(here(), CIC_ITEM,0xBBFFFF)

 


스크립트를 통해 findkey의 값을 찾아볼 것이다.

먼저 아이다에서 findkey를 실행시키고 흐름을 살펴본다.

what is key가 findkey의 단서이다.

what is key는 이미 변조가 다 되어있을테니까 변조가 되기전에 원래 값이 존재할 것이다.

 

분기가 4개가 있는데 어느 부분이 내려와서 변조가되는지 찾아봐야한다. 쉽게 생각해서

제일 처음에 있는 JNZ short 부분을 확인하면 된다.

4개 중에 sub_1이 있는 곳으로 들어가고 그 다음 sub_2, sub3으로 가고 마지막으로 sub_4에 간다

sub_4에 있는 enc_str이 값이 변조하게 된다. 좀 더 들어가면 해당 함수에서 XOR eax, 33h을 통해

값이 변조 된다.

그러면 변조되기 전의 값을 확인할 수 있는 방법은 2가지가 존재한다.

XOR eax, 33h 하기 전에 값을 가지고 오는 방법과

XOR eax, 33h을 한번 더 실행하여 복호화하여 값을 찾는 방법이 있다.

지금은 주소가 고정되어 있지만, 실질적으로 주소는 계속 바뀐다. 그렇기 때문에 이미지 들고와서

주소를 계산해주어야한다.

CTRL+S에 세션이 있는데 코드영역은 .text에 있다. 여기에 코드가 시작될 주소가 존재한다.

시작 주소에서 1000빼면 이미지 베이스구나 라고 생각해도 큰 상관은 없다.

실제로 디버깅하면서 보면 이미지 주소가 바뀔 수 있기 때문에 항상 위의 내용을 염두해야한다.

 

먼저 XOR eax,33h하기 전에 값을 가지고 오는 방법을 스크립트로 작성해본다.

* AddBpt() - 특정 주소에 BP 설정
* SetBptCnd() - 브레이크 포인트에 사용자 정의 핸들러 연결
* RunTo() - 기술한 위치에 도달하거나 BP만날때까지 진행 등

call en_str 시작하기전에 BP걸고 상대주소 값을 구하면된다. 상대주소 : 15B3
base_addr = idaapi.get_imagebase() // 이미지 베이스 값 구함
call_addr = 0x15b3 // 위 상대주소 구해서 넣음
addr = base_addr + call_addr

def get_string():
    addr = GetRegValue("EAX") -0x04
    print "value of EAX", hex(addr)
    out = ""
    while True:
        if Byte(addr) != 0: // NULL 만난게 아니라면 
            out += chr(Byte(addr)) // enc되기전에 배열에 담는 것
        else:
            break
        addr += 1
    print out // 담은거 출력
AddBpt(addr)
SetBptCnd(addr, "get_string()")

실행하면 자동으로 브레이크 포인트가 걸려야 하지만, 잘 안된다.

IDA 버전 이후로 트라이얼 버전은 디버거를 지원안해준다고 한다. 그래서 잘 동작이 안된다.

그래서 설정을 조금 바꿔줘야한다.

맨위에 디버거 - 셀렉터 디버거 - win32 디버거 선택을 해준다.

 

그러면 BP가 걸려있는 것을 확인할 수 있고 F9로 실행하면 해당 BP에서 멈추게 된다.

근데 get_string을 알수없다면서 실행이 안된다.

 

디버거 - Breakpoint - Breakpoint List - 우클릭 - edit - ... - scripting language를 Python으로 바꾼다.

 

그리고 다시 실행시켜 준다.

 

키 값인 BoAn!이 나오는 것을 확인했다.

이 방법은 XOR eax, 33h 하기전에 값을 가지고온 방법이다.


이 방법은 위의 방법과 다르게 XOR eax, 33h를 한번 더 해서 값을 찾는 방법이다.

base_addr = idaapi.get_imagebase()
call_addr = 0x1648

addr = base_addr + call_addr

def get_string():
    addr = GetRegValue("EAX")
    print "value of EAX", hex(addr)
    out = ""
   
    while True:
        if Byte(addr) != 0:
            out += chr(Byte(addr)^0x33)
            idc.PatchByte(addr, Byte(addr)^0x33)
        else:
            break
        addr += 1
   
    print out

AddBpt(addr)
SetBptCnd(addr, "get_string()")

 

똑같이 BoAn! 값이 나오는 것을 확인할 수 있음

call_addr이 다른 이유는 기존에 있던 XOR eax,33h가 완전히 끝난 상태에서

다시 XOR을 해주어야 하기때문에 프로그램이 끝나기 전에 주소에서 시작하기 위함이다.
* 1648 : whatisThekey?의 주소를 받아옴 // 해당 소스코드를 돌리고 BP 잡혔을때 스페이스바하면 보임
* 15b3 : enc_str이 시작하는 주소를 받아옴 // 이전 소스코드를 돌리고 bp잡혔을떄 스페이스바
* 단축키 : 스페이스바 누르면 그래프 <-> 코드 돌아가짐
              esc 누르면 전화면으로 넘어가짐

 


IDA 스크립트를 통해 findkey의 값을 구하는 것을 했다.

스크립트를 사용하기 위해서는 결국 전반적인 프로그램 분석이 되어야 활용할 수 있을거라 생각된다.

스크립트를 잘 활용하면 전반적인 프로그램 분석이 끝나고 취약점을 찾을 때나

처음에 취약한 함수를 찾거나 그냥 분석할때나 많은 도움이 될 것 같다.

 

+ Recent posts