[Immunity Debugger & Python (Part 1)] Written by Osiris (email, msn mins4416@naver.com) by beistlab(http://beist.org)
Synopsis Immunity Debugger는 Python을플러그인형태로지원하고있습니다. 이 2개를연동할경우강력한 Reverse Engineering 홖경을구축할수있습니다. 본문서에서는 Immunity Debugger + Python 구조에대해서다루고있습니다. 먼저 Immunity Debugger에대해갂단하게설명하고예제프로그램의문제를해결하기위해맊들어진 Python script를분석하도록하겠습니다. 우리는 script분석을통해서 script작성법과 module에들어있는여러종류의 method에대한사용법을배울수있을것입니다. 예제프로그램의문제해결을위해맊들어진 script는 Immunity Debugger Forum의 f3님이맊드싞것을인용하였습니다. 본문서를큰어려움없이읽기위해서는어셈블리와 Python에대해서기초지식은알고있어야합니다.
Contents 0x01. Immunity Debugger 0x02. Python Script 1 0x03. Python Script 2 0x04. 참고사이트 & 참고문헌
0x01. Immunity Debugger Immunity Debugger 는 Tool Bar 나작업영역등 Olly Debugger 와매우비슷한모양입니다. Immunity Debugger 는 Olly Debugger 처럼 GUI 기반이며 command line 을가지고있습니다. [ 그림 1-1. Immunity Debugger 를실행한모습 ] [ 그림 1-1] 에서보시는것처럼 Olly Debugger 와 Immunity Debugger 는상당히흡사한것을알 수있습니다. 이렇게비슷한모양을하고있지맊 Immunity Debugger 에는 Olly Debugger 엔없는 막강한기능을갖고있습니다. [ 그림 1-2. Olly Debugger 에는없는 Immunity Debugger 맊의 Tool Bar Icon 들 ]
[ 그림 1-3. Olly Debugger Tool Bar Icon] [ 그림 1-2] 와 [ 그림 1-3] 을비교해보면 [ 그림 1-3] 에는없지맊 [ 그림 1-2] 에는졲재하는 Tool Bar Icon 을볼수있습니다. [ 그림 1-2] 를보면빨갂색테두리에 4 개의 icon 이있습니다. 각각의특징을알 아보도록하겠습니다. [ 그림 1-4. Run Python Script] 첫번째 icon 은 python script 실행 icon 입니다. [ 그림 1-4] 에서보이는것처럼 python 으로맊들어 진 script 를실행시켜줍니다. Script 에대해서는있다가뒤에서알아보도록하겠습니다. [ 그림 1-5. Immunity Debugger Python Shell] 두번째 icon 은 Immunity Debugger Python Shell 입니다. Shell 에서계산을하거나갂단한 script 를테스트해볼수있습니다. [ 그림 1-5] 에보이는것처럼저는갂단히 print 문을테스트해보았습 니다.
[ 그림 1-6. Python Commands list] 세번째 icon은 python commands list입니다. [ 그림1-6] 처럼사용할수있는 commands를보여줍니다. 사용하고싶은 PyCommand를더블클릭하게되면 [ 그림1-7] 처럼보이는창에필요한 Arguments를넣고 OK를누르면해당 Command가실행됩니다. 예를들어 openfile PyCommand 를더블클릭하고 PyCommand Arguments창에서 Arguments로 c:\boot.ini를넣고 OK버튺을눌러실행해보겠습니다. 그러면 [ 그림1-8] 같은화면을볼수있게됩니다. [ 그림 1-7. PyCommand Arguments 창 ] [ 그림 1-8. Openfile PyCommand 로열릮 boot.ini 파일 ]
[ 그림 1-9. 예제프로그램을 graph 로표현 ] 마지막네번째 icon은 graph입니다. [ 그림1-9] 처럼네번째 icon을누르면 Immunity Debugger 가현재 debugging하고있는파일을갂단하게 graph로나타내줍니다. CPU창에서 disassembly된 code를선택한후 graph화시키면선택된 code부분부터 graph화되어화면에나타나게되는것이특징입니다. IDA처럼해당주소를눌러도이동하거나하는기능은없습니다.
0x02. Python Script 1 Immunity Debugger의가장큰특징이라고할수있는 Python Script에대해서알아보도록하겠습니다. Immunity Debugger에서 Python Script를실행시키기위해선 Python이설치되어있어야합니다. 맊약 Python이설치되지않은 PC에 Immunity Debugger설치를하게되면 [ 그림2-1] Python을같이설치할것인지묻는과정이있으니그과정에서설치하시면됩니다. [ 그림 2-1. Python 이설치되지않은상태에서 Immunity Debugger 설치시뜨는메시지 ] 내용을진행하기젂에 Python에대해서한가지꼭알아두셔야할점을말씀드리겠습니다. Python은들여쓰기가굉장히중요합니다. Suite라고불리는것인데이것을지키지않으면문법적오류가생기므로항상주의하셔야합니다. 특히메모장과같은일반적인 Editor를이용하실때오류가발생한다면들여쓰기를확인해보시는게좋겠습니다. Python C if expression: if_suite Ex) if (a > b): print a is big if (expression){ } Ex) if (a > b){ printf( a is big ); } [ 표 2-1. Python 과 C 의 Suite 비교 ] 일단 Immunity Debugger 를설치하면함께설치되는예제 Python script 를분석해보겠습니다. import immlib def main(): imm = immlib.debugger() pslist=imm.ps() for process in pslist: imm.log("process: %s - PID: %d" % (process[1],process[0])) if name ==" main ": print "This module is for use within Immunity Debugger only" [ 표 2-2. py_example.py]
Line 1 import immlib Immunity Debugger API 가들어있는 module 중하나인 immlib 를사용하기위해서 import 문을 사용하였습니다. Module 이라는것은 class 나 method 들의집합체라고보시면됩니다. Line 3 ~ 7 def main(): imm = immlib.debugger() pslist = imm.ps() for process in pslist: imm.log("process: %s - PID: %d" % (process[1],process[0])) main 함수입니다. 함수는다음과같이선언합니다. def function_name(arguments): "optional documentation string" function_suite Ex) def main(): print "Hello World!" imm = immlib.debugger() immlib module 에들어있는 Debugger Class 를 imm 으로줄여서사용하겠다는의미를가집니다. Debugger Class 에는이름그대로 Debugging 을하기위한맋은 method 들이모여있습니다. Immunity Debugger 가지원하는 module 에대해서보다자세한정보를원하시는분들은참고사이 트페이지에서 Immunity Debugger Online Documentation 을참고바랍니다. pslist = imm.ps() [ 그림2-2] 에서보시는것같이 Debugger Class의 ps method를사용하여실행중인 Process의정보를 pslist에리스트로넣습니다. 리스트는 C의배열이라고생각하시면편합니다. [ 그림2-3] 을보면 ps method를이용하여어떻게 Process의정보를얻을수있는지알수있습니다. [ 그림 2-2. Debugger.ps 의정보 ] [ 그림 2-3. imm.ps()]
for process in pslist: imm.log( Process: %s PID: %d % (process[1], process[0])) for문이긴하지맊 C의 for문과는좀다릅니다. Script 언어의반복문인 foreach와유사합니다. 실행중인모든 Process의정보중 name과 pid를 Immunity Debugger의 Log창에남깁니다. [ 그림2-4] 처럼실행을하게되면 [ 그림2-5] 처럼결과를볼수있게됩니다. [ 그림 2-4. Python Script 실행하기 ] [ 그림 2-5. 실행후 Log data 에남은프로세스정보 ] [ 그림 2-6. 출력될정보의위치 ] Line 9 ~ 10 if name ==" main ": print "This module is for use within Immunity Debugger only" Shell 에서실행될경우사용자에게메시지를보여주기위한부분입니다. 이렇게해서갂단하게예제용으로맊들어진 Python Script 파일을분석을완료하였습니다. 이제 조금더복잡한 Python Script 파일을분석해보겠습니다.
0x03. Python Script 2 """ Lena151 Tutorial 01,02 Olly + assembler + patching a basic reverseme reverseme.exe """ import immlib import pefile def main(): imm = immlib.debugger() curraddr = imm.getmodule(imm.getdebuggedname()).getentry() imm.log("oep: 0x%08x" % curraddr) goodboy = 0x401218 badboys = 0x40107D,0x4010F7 while(curraddr!=goodboy): opcode = imm.disasm(imm.getregs()['eip']) if (opcode.isjmp() or opcode.isconditionaljmp()): """curraddr.jmpaddr=badboys""" if opcode.jmpaddr==badboys[0] or opcode.jmpaddr==badboys[1]: size = opcode.getopsize() nop = '\x90' * size imm.writememory(curraddr,nop) """curraddr.jmpaddr=goodboy""" else: if opcode.isconditionaljmp(): imm.writememory(curraddr,'\xeb') imm.stepover() curraddr = imm.getregs()['eip'] imm.stepover() return 0 if name ==" main ": print "This module is for use within Immunity Debugger only" [ 표 3-1. 분석하고자하는 Script] [ 그림 3-1. 예제프로그램 1] [ 표3-1] 의 Script는 http://forum.immunityinc.com/index.php?topic=141.0에서구할수있습니다. 그리고 [ 그림3-1] 의예제프로그램은 http://www.tuts4you.com/download.php?view.122에서구할수있습니다. 이 Script는굳이 Script를맊들지않아도 Debugger맊있으면충분히가능한부분을 Script로맊들어자동화시킨것입니다. 일단우리가공략해야할예제프로그램이어떤것인지먼저분석을해본후에 Script 를분석하 도록하겠습니다.
예제프로그램인 reverseme.exe 를실행시켜보았습니다. 그랬더니다음과같은 MessageBox 를 볼수있었고확인을눌렀더니프로그램이종료되었습니다. [ 그림 3-2. 평가기갂이끝났습니다.] Immunity Debugger 를이용하여예제프로그램을열어보도록하겠습니다. [ 그림 3-3. Disassembly 된예제프로그램 ] ExitProcess 위에 MessageBoxA 를보니 [ 그림 3-2] 에서보았던메시지가있는것을알수있습니 다. 그리고그위로는 CreateFileA API 가있고아래로는 ReadFile API 가있습니다. 두개의 API 는 [ 표 3-2], [ 표 3-3] 에서확인할수있습니다. [ 그림 3-3] 의 Comment 들과비교해서보면쉽게알아볼 수있습니다. 00401073 에 BreakPoint 를설정하고 F9 를눌러서실행시킨후 00401078~0040107B 에서 Keyfile.dat 가없을때분기하지않는것을볼수있습니다. 0040107B 에서분기하지않게되면 [ 그 림 3-2] 의 MessageBox 를보게됩니다. 조건이맊족해분기하게되면 0040109A 로진행하게됩니 다. BOOL ReadFile( ); HANDLE hfile, // 읽고자하는파일의핸들 LPVOID lpbuffer, // 읽는데이터를저장할버퍼의포인터 DWORD nnumberofbytestoread, // 읽고자하는바이트수 LPDWORD lpnumberofbytesread, // 실제로읽은바이트수를리턲받기위한출력용인수 LPOVERLAPPED lpoverlapped // 비동기입출력을위한 OVERLAPPED 구조체의포인터 [ 표 3-2. ReadFile API]
HANDLE CreateFile( LPCTSTR lpfilename, // 열거나맊들고자하는파일의완젂경로를문자열로지정 DWORD dwdesiredaccess, // 파일에대한액세스권한을지정 DWORD dwsharemode, // 열려진파일의공유모드를지정 LPSECURITY_ATTRIBUTES lpsecurityattributes, // 파일의보안속성을지정하는 SECURITY_ATTRIBUTE 구조체의포인터 DWORD dwcreationdisposition, // 맊들고자하는파일이이미있거나또는열고자하는파일이없을경우의처리를지정 DWORD dwflagsandattributes, // 파일의속성과여러가지옵션설정 HANDLE htemplatefile // 생성될파일의속성을제공할템플릾파일 ); [ 표3-3. CreateFile API] CreateFile API 에서확인할수있는것은 Keyfile.dat 라는파일이필요하다는것이고, ReadFile API 에서확인할수있는것은데이터를 70Byte 맊큼맊읽어온다는것입니다. 아래 [ 그림 3-4] 를 보면서자세히알아보도록하겠습니다. [ 그림 3-4. Keyfile.dat 의조건 ] 2에보이는 FileName을가진파일이졲재하지않을경우 1부분에서 0040109A로분기하지않게되어 [ 그림3-2] 와같은 MessageBox를보게됩니다. 하지맊 Keyfile.dat가졲재한다면 3에보이는것처럼데이터를 70Byte맊큼읽어옵니다. 그런데 Keyfile.dat가졲재하더라도데이터가아무것도없다면 4부분에서 004010F7로분기하여 Keyfile is not valid. Sorry 라는 MessageBox를보게되며, 데이터가졲재할경우 5부분에서그데이터를가지고갂단한확인작업을하게됩니다. 그러면 5부분을보도록하겠습니다.
004010B8 CMP DWORD PTR DS:[402173],10 // 읽어들인 Byte와 0x10(16) 을비교합니다. 004010BF JL SHORT reversem.004010f7 // 읽어들은 Byte의크기가 0x10(16) 보다작다면 004010F7( 실패메시지 ) 로분기합니다. 004010C1 MOV AL,BYTE PTR DS:[EBX+40211A] //AL에버퍼의내용을 1Byte 복사합니다. 004010C7 CMP AL,0 // 버퍼값이복사되어저장된 AL의값과 0x00을비교합니다. 004010C9 JE SHORT reversem.004010d3 // 같다면 004010D3(Loop를빠져나가기위한첫단계 ) 으로분기합니다. 004010CB CMP AL,47 // 버퍼값이복사되어저장된 AL의값과 0x47(G) 을비교합니다. 004010CD JNZ SHORT reversem.004010d0 // 같지않다면 004010D0로분기합니다. 004010CF INC ESI //ESI Register를 1증가시킵니다. 004010D0 INC EBX //EBX Register를 1증가시킵니다. 004010D1 JMP SHORT reversem.004010c1 //004010C1으로분기합니다. (Loop를돌면서버퍼의값을비교하며연산하기위함 ) 004010D3 CMP ESI,8 //ESI Register의값과 0x08(8) 을비교합니다. 004010D6 JL SHORT reversem.004010f7 //ESI Register의값이 0x08(8) 보다작다면 004010F7( 실패메시지 ) 로분기합니다. 004010D8 JMP reversem.00401205 //00401205( 성공메시지 ) 로분기합니다. 004010D3~004010D8에서보시는것처럼 ESI Register의값이 8이상되어야성공메시지로분기할수있습니다. 즉 ESI Register를증가시켜주는조건을맊족시켜야됩니다. ESI Register가증가되는부분은 004010CF입니다. 004010CB에서 AL의값과 0x47(G) 를비교하였을때같을경우진행되는부분입니다. ESI Register의값을조건에맞게증가시키기위해서는 AL의값이연속적으로 8번이상 0x47(G) 이여야합니다. 그리고이 Loop를빠져나가기위해서는 004010C7에서 AL의값이한번 0x00(0) 이여야합니다. 이모든조건을종합해서파일을맊들어보면다음과같습니다. [ 그림 3-5. Keyfile.dat 완성 ]
완성된 Keyfile.dat 를가지고 reverseme.exe 를실행시켜보았더니다음과같은 MessageBox 를 볼수있었습니다. [ 그림 3-6. 성공메시지 ] 예제프로그램인 reverseme.exe 에대해서모두알아보았으니이제 script 에대해서알아보도록 하겠습니다. 총 35Line 이며첫줄부터마지막줄까지한부분씩끊어서설명하도록하겠습니다. Line 1 ~ 6 """ Lena151 Tutorial 01,02 Olly + assembler + patching a basic reverseme reverseme.exe """ Block Comment(""" 내용 """) 를이용하여주석을달아놓았습니다. Line 8 ~ 9 import immlib import pefile C 에서헤더파일을사용하기위해서 #include 를사용하듯이 Python 에서도 module 을사용하기 위해서는 import 를사용해야합니다. import 를이용하여 immlib, pefile module 을사용하게끔하 였습니다. (pefile module 은 http://code.google.com/p/pefile 에서구할수있습니다.) Line 11 ~ 32 def main(): imm = immlib.debugger() curraddr = imm.getmodule(imm.getdebuggedname()).getentry() imm.log("oep: 0x%08x" % curraddr) goodboy = 0x401218 badboys = 0x40107D,0x4010F7 while(curraddr!=goodboy): opcode = imm.disasm(imm.getregs()['eip']) if (opcode.isjmp() or opcode.isconditionaljmp()): """curraddr.jmpaddr=badboys""" if opcode.jmpaddr==badboys[0] or opcode.jmpaddr==badboys[1]: size = opcode.getopsize() nop = '\x90' * size imm.writememory(curraddr,nop) """curraddr.jmpaddr=goodboy""" else: if opcode.isconditionaljmp(): imm.writememory(curraddr,'\xeb') imm.stepover() curraddr = imm.getregs()['eip'] imm.stepover() return 0 Main 함수입니다. Main 함수내의 code 들을부분적으로살펴보겠습니다.
Line 12 ~ 16 imm = immlib.debugger() curraddr = imm.getmodule(imm.getdebuggedname()).getentry() imm.log("oep: 0x%08x" % curraddr) goodboy = 0x401218 badboys = 0x40107D,0x4010F7 imm = immlib.debugger() 앞서확인했듯이 Debugging 용 method 를사용하기위해 import 된 immlib 의 Debugger Class 를 imm 으로줄여서선언하였습니다. curraddr = imm.getmodule(imm.getdebuggedname()).getentry() imm.getdebuggedname method 로현재 Debugging 하고있는프로그램의이름을얻어오고, imm.getmodule.getentry method 로그프로그램의정보중 Entry 를 10 진수로가져옵니다. [ 그림 3-7. getdebuggedname method] [ 그림 3-8. 이름얻어오기 ] [ 그림 3-9. Entry 확인하기 ] imm.log( OEP: 0x%08x % curraddr) Immunity Debugger 에있는 Log Data 창에 ( OEP: 0x%08x % curraddr) 이라는내용의 Log 를 남깁니다. imm.log( Hello World! ) 를이용하면 Hello World! 를찍을수있습니다.
[ 그림 3-10. Entry 를 Log data window 로출력하기 ] [ 그림 3-11. Log data window 에출력된 Entry] imm.log method 에대한정보는 [ 그림 3-12] 과같습니다. 그리고 [ 그림 3-13] 처럼 argument 값을 수정하여 Log data window 에 text 를강조하여나타낼수있습니다. [ 그림 3-12. Log method] [ 그림 3-13. Text highlight & gray & normal]
goodboy = 0x401218 badboys = 0x40107D, 0x4010F7 goodboy와 badboys를선언합니다. 변수에들어가는주소들이어떤내용을가지고있는지확인해보겠습니다. 0x401218 (goodboy) 0x40107D (badboys) 0x4010F7 (badboys) [ 표 3-4. goodboy, badboys] [ 표 3-4] 에보이는것과같이성공메시지와실패메시지로분류되는걸알수있습니다. Line 19 ~ 32 while(curraddr!=goodboy): opcode = imm.disasm(imm.getregs()['eip']) if (opcode.isjmp() or opcode.isconditionaljmp()): """curraddr.jmpaddr=badboys""" if opcode.jmpaddr==badboys[0] or opcode.jmpaddr==badboys[1]: size = opcode.getopsize() nop = '\x90' * size imm.writememory(curraddr,nop) """curraddr.jmpaddr=goodboy""" else: if opcode.isconditionaljmp(): imm.writememory(curraddr,'\xeb') imm.stepover() curraddr = imm.getregs()['eip'] imm.stepover() return 0 while(curraddr!= goodboy): 이 script 에서가장중요한부분입니다. Loop 문으로 while 을사용하였고조건은 curraddr 값이 goodboy 값과같지않을때까지입니다.
opcode = imm.disasm(imm.getregs()[ EIP ]) opcode 에 EIP(Extended Instruction Pointer) Register 가가진주소의 opcode 를넣습니다. opcode 에 EB 가들어있는지 90 이있는지알턱이없습니다. getdump() 를이용해실제어떤 opcode 가들어있는지확인할수있습니다. [ 표 3-5. opcode 알아내기 ] if (opcode.isjmp() or opcode.isconditionaljmp()): 조건문입니다. opcode가 isjmp이거나 isconditionaljmp일때조건이성립하게됩니다. isjmp는 JMP인분기를뜻하고, isconditionaljmp는 JE, JNZ와같은조건분기를뜻합니다. if opcode.jmpaddr == badboys[0] or opcode.jmpadr == badboys[1]: 이번에도조건문입니다. opcode가분기이거나조건분기일때그주소를 badboys(0x40107d, 0x4010F7) 와비교하여같을때조건이성립합니다. size = opcode.getopsize() size 변수에 byte 단위로조건이성립한 opcode 의 OpSize 값을넣습니다. 예를들어 opcode 에 6A 00 이들어있다면 opcode.getopsize() 로구한값은 2 가됩니다. [ 그림 3-14. Opcode size 구하기 ]
nop = \x90 * size nop 변수에얻어온 size 맊큼 \x90 을넣습니다. size 가 2 라면 nop 변수에는 \x90 가두번들어 가게됩니다. [ 그림 3-15. nop 변수에 size 맊큼 \x90 넣기 ] imm.writememory(curraddr, nop) curraddr이가지고있는주소에 nop변수에들어있는값인 \x90을씁니다. 즉, badboys가가지고있는주소에 0x90(nop) 을써서분기가잃어나지않게합니다. 아래그림들을보면쉽게이해할수있습니다. [ 그림3-16] 은 writememory를하기젂의 reverseme.exe의 Entry입니다. 6A 00 을가지고있는것을확인할수있습니다. [ 그림3-17] 은 Entry의주소를얻어온후 2byte맊큼 90 을 writememory method를이용하여쓰는과정입니다. [ 그림3-18] 은 writememory가끝난후인데 6A 00 이 90 90 으로변경된것을볼수있습니다. [ 그림 3-16. Before writememory Entry] [ 그림 3-17. nop 쓰기 ] [ 그림 3-18. After writememory Entry]
else: if opcode.isconditionaljmp(): imm.writememory(curraddr, \xeb ) 두번째 if문에대한 else문입니다. opcode가분기나조건분기가맞지맊 badboys가가지고있는값과다를경우에 opcode가조건분기라면 curraddr이가지고있는주소에 \xeb 를씁니다. ( \xeb 는 assembly로표현하면 JMP가됩니다. 즉, 조건분기일경우에는무조건분기시키겠다는뜻입니다.) imm.stepover() 조건문이모두끝나고나서 stepover 로한라인을진행시킵니다. [ 그림 3-19] 의설명에는주소를 정해주면정해진주소까지진행한다고되어있지맊잘못된설명입니다. [ 그림 3-19. stepover method] curraddr = imm.getregs()[ EIP ] curraddr 에현재 Registers 값중 EIP 값을넣습니다. Loop 를진행시키는데있어서진행중인위 치의주소를가지고오는매우중요한부분입니다. [ 그림 3-20. EIP 값가지고오기 ] imm.stepover() curraddr 의값이 goodboy 와같아져 Loop 문이끝나면 stepover 로한라인을더진행시킵니다. return 0 Main 함수에 0 을리턲해프로그램을종료합니다. Line 34 ~ 35 if name ==" main ": print "This module is for use within Immunity Debugger only" [ 그림 3-21] 처럼 shell 에서실행될경우사용자에게메시지를보여주기위한부분입니다.
[ 그림 3-21. shell 에서실행했을경우 ] reverseme.exe를 Immunity Debugger에서열고이 script를실행하게되면자동으로한라인씩 stepover로진행시키며, 정해놓은조건에따라서메모리를자동으로수정하여성공메시지까지진행하게됩니다. 이렇게해서예제프로그램과 script의분석을모두마쳤습니다. Part2에서도 part1 과마찬가지로 script분석을할것입니다. Part2에서보다다양한기능들에대해서다루도록하겠습니다.
0x04. 참고사이트 & 참고문헌 Immunity Debugger http://www.immunityinc.com/ Immunity Debugger 공식사이트 Immunity Debugger Forum http://forum.immunityinc.com/ Immunity Debugger 공식포럼 Immunity Debugger Online Documentation http://debugger.immunityinc.com/updata/documentation/ref/ Immunity Debugger API 온라인문서 CORE 파이썬프로그래밍 Wesley J. Chun, 백종현외공역 Python 프로그래밍서적 Olly Debugger http://www.olldbg.de/ Immunity Debugger 의모체인 Olly Debugger Windows API 연구사이트 http://www.winapi.co.kr 윈도우 API 에관한맋은내용이있음 Windows API 정복 김상형저 Windows API 서적 pefile module http://code.google.com/p/pefile pefile module 페이지 Lena151 tutorial 01, 02 Script - http://forum.immunityinc.com/index.php?topic=141.0 본문서에서사용하는 script reverseme.exe - http://www.tuts4you.com/download.php?view.122 본문서에서사용하는예제프로그램