깔끔하게 보고 싶으신 분들은 위 pdf파일을 받아주시길..
제일 간단하게 프로그램의 Instruction을 추적하는 Pintool을 만들어 보겠다. Pintool을 만든다는 것은 Pin의 플러그인을 만든다는 느낌으로 만들면 된다. Pin은 JIT의 형태로 생각하면 된다. 자세한 것은 매뉴얼을 참고하자.
[1] instruction 추적하기
일단 타겟 프로그램을 다음과 같이 작성한다.
패스워드를 입력받고 0x31337이면 cong를 출력하는 프로그램이다. 다음과 같이 Main부분의 instruction만을 뽑아오도록 간단한 핀툴을 만든다.
#include <stdio.h> #include <iostream> #include "pin.H" using namespace std; #define START 0x4005FD #define END 0x40065F INT32 Usage() { PIN_ERROR("this pintool print a trace of memory address\n"); return -1; } void printip(void* ip) { printf("address: %p\n",ip); } void Instruction(INS ins, VOID* v) { long int a = (long int)INS_Address(ins); if(START<=a && a<=END) { printf("Instruction \n"); INS_InsertCall(ins,IPOINT_BEFORE, (AFUNPTR)printip, IARG_INST_PTR, IARG_END); } } void Fini(INT32 code, VOID *v) { cout << "Fini code: " << code << endl; } int main(int argc,char* argv[]) { if(PIN_Init(argc,argv)) return Usage(); INS_AddInstrumentFunction(Instruction,0); PIN_AddFiniFunction(Fini,0); PIN_StartProgram(); return 0; } |
}
코드에 대해 설명하자면 main에서 INS_AddInstrumentFunction 함수로 각 instruction이 실행될 때 실행되는 코드를 등록한다. Instruction함수를 보면 INS_Address로 각각 instruction이 실행될 때의 주소 값을 구해 main함수 내의 instruction의 주소 값만 뽑아온다. 또 각 instruction에 대해 INS_InsertCall을 이용해 instruction이 실행되기 전에 호출되는 callback 함수를 등록한다. 여기선 printip을 등록한다 printip에 넘어가는 인자는 IARG_INST_PTR이라는 인자가 넘어가는데 매뉴얼을 참고해 보면 이건 instruction의 주소 값이다.
다음과 같이 make.rules를 변경하고 make로 컴파일해서 돌려보자.
필자는 hyper.cpp이라는 이름으로 저 소스코드를 저장하였고 따라서 위와 같이 make 룰을 변경하였다. 그 다음 make를 통해 컴파일 하자.
그리고 다음과 같이 실행시키자.(root로 실행하여야 한다.)
이것과 ida로 본 instruction을 하나하나 비교해보자
위의 결과를 비교한 결과 내릴 수 있는 결론은 다음과 같다. 0x4005fd~400611까지 한 묶음, 0x40061B~0x400627까지 한 묶음, 0x400631~0x400639까지 한 묶음…[즉 BBL단위로 묶음] 이런 식으로 routine이 분기되기 전까지 Instruction함수가 호출되고 분기되고 난 후 INS_InsertCall() 으로 등록한 printip함수가 모조리 호출된다.
[2] 비밀번호 맞추기
위와 같이 instruction의 Opcode가 CMP이면 해당 오퍼렌드의 두번 째 비교 값을 가져와서 뿌려준다. 결과는 다음과 같다.
#include <stdio.h> #include <iostream> #include "pin.H" using namespace std; #define START 0x4005FD #define END 0x40065F INT32 Usage() { PIN_ERROR("this pintool print a trace of memory address\n"); return -1; } void Instruction(INS ins, VOID* v) { long int a = (long int)INS_Address(ins); if(START<=a && a<=END) { if(INS_Opcode(ins) == XED_ICLASS_CMP ) { UINT64 value = INS_OperandImmediate(ins,1); printf("value is 0x%lx \n",value); } } } void Fini(INT32 code, VOID *v) { cout << "Fini code: " << code << endl; } int main(int argc,char* argv[]) { // Pin initiate if(PIN_Init(argc,argv)) return Usage(); INS_AddInstrumentFunction(Instruction,0); PIN_AddFiniFunction(Fini,0); PIN_StartProgram(); return 0; |
[3] [2013 Plaid ctf] hypercomputer-1(bin 100)
문제 파일을 열어보면 다음과 같다.
hello함수에서 배너를 출력하고 CalcResult에서 원래 문자열을 조작해서 flag를 만들어 둔다. CalcResult나 다른 함수들을 리버싱 하면 알겠지만 쓸대없이 usleep과 많은 while문,for문을 만나게 된다. 따라서 핀툴로 이러한 루틴을 다 없애주면 flag를 줄것이다 J 아래의 코드는 그 조작을 한 핀툴을 작성한 것.
#include "pin.H" #include <iostream> #include <fstream> #include <unistd.h> int new_usleep(useconds_t usec) { return 0; } void Image(IMG img, void *v) { for(SYM sym = IMG_RegsymHead(img); SYM_Valid(sym); sym = SYM_Next(sym)) { string funcname = PIN_UndecorateSymbolName(SYM_Name(sym),UNDECORATION_NAME_ONLY); if(funcname == "usleep") { RTN rtn = RTN_FindByAddress(IMG_LowAddress(img)+SYM_Value(sym)); if(RTN_Valid(rtn)) { RTN_Open(rtn); RTN_Replace(rtn, (AFUNPTR) &new_usleep); RTN_Close(rtn); } } } } void xor_rax(CONTEXT *ctx) { PIN_SetContextReg(ctx, REG_RAX,0); } void mov_rax_rdx(CONTEXT* ctx) { ADDRINT rdx = PIN_GetContextReg(ctx, REG_RDX); PIN_SetContextReg(ctx, REG_RAX, rdx); } void delete_insns(BBL bbl) { for(INS ins = BBL_InsHead(bbl); INS_Valid(ins); ins = INS_Next(ins)) { INS_Delete(ins); } } VOID Trace(TRACE trace, VOID *v) { for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl)) { if(BBL_NumIns(bbl) == 2) { INS ins1 = BBL_InsHead(bbl); INS ins2 = INS_Next(ins1); if(INS_Disassemble(ins1) == "sub rax, 0x1" && INS_IsBranch(ins2) && INS_DirectBranchOrCallTargetAddress(ins2) == BBL_Address(bbl)) { BBL_InsertCall(bbl, IPOINT_BEFORE, (AFUNPTR) &xor_rax, IARG_CONTEXT, IARG_END); delete_insns(bbl); } } else if(BBL_NumIns(bbl) == 3) { INS ins1 = BBL_InsHead(bbl); INS ins2 = INS_Next(ins1); INS ins3 = INS_Next(ins2); if(INS_Disassemble(ins1) == "sub rax, 0x1" && INS_IsNop(ins2) && INS_DirectBranchOrCallTargetAddress(ins3) == BBL_Address(bbl)) { BBL_InsertCall(bbl, IPOINT_BEFORE, (AFUNPTR) &xor_rax, IARG_CONTEXT, IARG_END); delete_insns(bbl); } else if(INS_Disassemble(ins1) == "add rax, 0x1" && INS_Disassemble(ins2) == "cmp rax, rdx" && INS_DirectBranchOrCallTargetAddress(ins3) == BBL_Address(bbl)) { BBL_InsertCall(bbl, IPOINT_BEFORE, (AFUNPTR) &mov_rax_rdx, IARG_CONTEXT,IARG_END); delete_insns(bbl); } } } } int main(int argc, char* argv[]) { if(PIN_Init(argc,argv)) { return 0; } PIN_InitSymbols(); IMG_AddInstrumentFunction(Image,0); TRACE_AddInstrumentFunction(Trace,0); PIN_StartProgram(); return 0;} |
}
실행하면 다음과 같다.
이 문제의 설명은 다음의 원문에 잘 설명되어 있다: https://eindbazen.net/2013/04/pctf-2013-hypercomputer-1-bin-100/
'Analysis' 카테고리의 다른 글
Pin 기본 API살펴보기 (0) | 2015.08.07 |
---|---|
[smt solver]z3py 튜토리얼 (0) | 2015.08.06 |
Pin tool 기본세팅 (1) | 2015.07.17 |