Reversing@radare2# A journey into Radare 2 – Part 1: Simple crackme
Radare2
radare2는 리버스 엔지니어링 및 바이너리 분석을 위한 오픈 소스 프레임워크로 디스어셈블, 데이터 분석, 바이너리 패치, 데이터 비교, 검색, 대체, 시각화 등을 위해 많은 명령행 인터페이스를 구현합니다. 좋은 스크립팅 기능을 갖추고 있으며 모든 주요 플랫폼 (GNU / Linux, Windows * BSD, iOS, OSX, Solaris ...)에서 실행되며 다양한 아키텍처와 파일 형식을 지원합니다.
Getting Radare2
Installation
$ git clone https://github.com/radare/radare2.git $ cd radare2 $ ./sys/install.sh |
git 버전을 설치하지 않거나 다른 OS(Windows, OS X, iOS 등) 용 바이너리를 원할 경우 radare2 웹 사이트에서 다운로드 페이지를 확인하십시오.
Updating
전에 말했듯이 항상 최신 버전의 r2를 git 저장소에서 사용하는 것이 좋습니다. git에서 r2 버전을 업데이트하려면 다음을 실행하기만 하면 됩니다.
$ ./sys/install.sh |
Uninstalling
여러분이 초기에 radare2를 제거하는 이유를 이해할 수 없지만 간단히 다음 명령어를 실행하여 삭제할 수 있습니다. :
$ make uninstall $ make purge |
Getting Started
우리의 첫번째 챌린지는 이곳에서 다운로드 할 수 있습니다.
radare2가 시스템에 설치되었고 바이너리를 다운로드 했으므로 이제 radare2의 기본 사용법을 탐색할 준비가 되었습니다. Remnux 컴퓨터에서 작업하겠습니다. 하지만 대부분의 명령과 설명 (모두는 아니더라도)은 Windows 컴퓨터와 다른 컴퓨터에서 동일합니다.
Command Line Arguments
대부분의 명령 행 유틸리티에서 사용 가능한 인자의 목록을 표시하는 가장 좋은 방법은 -h 플래그로 프로그램을 실행하는 것 입니다.
$ r2 -h |
여기에 -h 실행 시의 전체 출력을 붙여 넣지는 않겠습니다. 대신에 주로 사용하는 것들을 확인해보겠습니다.
Usage: r2 [-ACdfLMnNqStuvwz] [-P patch] [-p prj] [-a arch] [-b bits] [-i file] [-s addr] [-B baddr] [-M maddr] [-c cmd] [-e k=v] file|pid|-|--|= -d: Debug the executable 'file' or running process 'pid' -A: Analyze executable at load time (xrefs, etc) -q: Quiet mode, exit after processing commands -w: Write mode enabled -L: List of supported IO plugins -i [file]: Interprets a r2 script -n: Bare load. Do not load executable info as the entrypoint -c 'command; command; ...': Run r2 and execute commands (eg: r2 's main; px 60') -p [prj]: Creates a project for the file being analyzed -: Opens r2 with the malloc plugin that gives a 512 bytes memory area to play with |
Binary info
새로운 챌린지에 직면 할 때마다 먼저 바이너리에 대한 정보를 얻고 싶습니다. r2 프레임 워크에서 가장 강력한 도구 중 하나 인 rabin2를 사용하여 압축을 풀어 보겠습니다.
rabin2는 Sections, Headers, Imports, Strings, Entrypoints 등의 바이너리 파일에서 정보를 추출할 수 있습니다. 그런 다음 출력을 여러 형식으로 내보낼 수 있습니다. rabin2는 ELF, PE, Mach-O, Java CLASS와 같은 많은 파일 형식을 이해할 수 있습니다.
자세한 정보는 man rabin2를 확인하십시오.
운영 체제, 언어, 엔디안, 아키텍처, mitigations(canary, pic, nx) 등의 바이너리 정보를 출력하는 -I 플래그를 사용하여 rabin2를 호출합니다.
$ rabin2 -I megabeets_0x1 havecode true pic false canary false nx false crypto false va true intrp /lib/ld-linux.so.2 bintype elf class ELF32 lang c arch x86 bits 32 machine Intel 80386 os linux minopsz 1 maxopsz 16 pcalign 0 subsys linux endian little stripped false static false linenum true lsyms true relocs true rpath NONE binsz 6220 | cs |
명확하게 알 수 있듯이 바이너리는 32 비트 ELF 파일로, 스트립되어 있지 않고 동적으로 링크되어 있습니다. exploit mitigation 기술은 없습니다. 다음 글에서 유용한 정보는 radare2를 사용하여 exploit하는 방법을 학습할 때 유용합니다.
이제 프로그램을 실행하고 프로그램이 무엇을 하는지 봅시다.
$ ./megabeets_0x1 .:: Megabeets ::. Think you can make it? Nop, Wrong argument. $ ./megabeets_0x1 abcdef .:: Megabeets ::. Think you can make it? Nop, Wrong argument. |
처음으로 우리가 바이너리를 실행 시킬 때 "Nop, Wrong argument"라는 메시지를 확인할 수 있습니다. 해당 바이너리에 대해 인자를 제공할 필요가 있다고 가정한다면 이번에는 'abcdef'를 인자로 사용하여 다시 실행한 결과 우리는 다시 실패했습니다. 분명히 이 암호를 푸는 데는 몇 가지 암호가 필요합니다.
radare2를 사용해 바이너리를 살펴봅시다.
$ r2 ./megabeets_0x1 — Thank you for using radare2. Have a nice night! [0x08048370]> |
radare2 쉘을 생성하고 자동으로 생성되는 메세지를 확인할 수 있습니다.
[0x08048370]> ie [Entrypoints] vaddr=0x08048370 paddr=0x00000370 baddr=0x08048000 laddr=0x00000000 haddr=0x00000018 type=program1 entrypoints |
바이너리의 진입점(Entry Point)을 확인하는 ie 명령을 사용했습니다. r2 명령어들은 각각 의미있는 문자들입니다. 이 예에서 ie는 info >> entrypoint를 나타냅니다. 그렇기 때문에 radare2 기능에 익숙해지면 명령어를 쉽게 기억할 수 있습니다. 그러나 모든 명령어들을 기억할 필요는 없습니다. 간단하게 ?를 추가하면 대부분 모든 명령어의 기능과 하위 명령어들에 대한 정보를 확인할 수 있습니다.
[0x08048370]> i? |Usage: i Get info from opened file (see rabin2’s manpage) | Output mode: | ‘*’ Output in radare commands | ‘j’ Output in json | ‘q’ Simple quiet output | Actions: | i|ij Show info of current file (in JSON) | iA List archs | ia Show all info (imports, exports, sections..) | ib Reload the current buffer for setting of the bin (use once only) | ic List classes, methods and fields | iC Show signature info (entitlements, …) | id Debug information (source lines) | iD lang sym demangle symbolname for given language | ie Entrypoint | iE Exports (global symbols) | ih Headers (alias for iH) | iHH Verbose Headers in raw text | ii Imports | iI Binary info | ik [query] Key-value database from RBinObject | il Libraries | iL List all RBin plugins loaded | im Show info about predefined memory allocation | iM Show main address | io [file] Load info from file (or last opened) use bin.baddr | ir|iR Relocs | is Symbols | iS [entropy,sha1] Sections (choose which hash algorithm to use) | iV Display file version info | iz Strings in data sections | izz Search for Strings in the whole binary | iZ Guess size of binary program |
i 명령어는 열린 파일에서 정보를 얻는 것을 목표로합니다. 실제로 radare2 쉘에서 구현 된 rabin2입니다.
Analysis
radare2는 대용량 파일을 처리 할 때 특히 오랜 시간이 걸리는 복잡한 프로세스이기 때문에 기본적으로 파일을 분석하지 않습니다. 시작시 분석을 수행하지 않는 분석과 선택에 대해 더 읽으려면 radare2 블로그에서 이 게시물을 읽을 수 있습니다.
Obviously analysis은 여전히 가능하며 r2에는 많은 분석 유형이 있습니다. 이전에 언급했듯이 'a'명령에 '?'를 추가하여 분석 옵션을 탐색 할 수 있습니다.
[0x08048370]> a? |Usage: a[abdefFghoprxstc] […] | ab [hexpairs] analyze bytes | abb [len] analyze N basic blocks in [len] (section.size by default) | aa[?] analyze all (fcns + bbs) (aa0 to avoid sub renaming) | ac[?] [cycles] analyze which op could be executed in [cycles] | ad[?] analyze data trampoline (wip) | ad [from] [to] analyze data pointers to (from-to) | ae[?] [expr] analyze opcode eval expression (see ao) | af[?] analyze Functions | aF same as above, but using anal.depth=1 | ag[?] [options] output Graphviz code | ah[?] analysis hints (force opcode size, …) | ai [addr] address information (show perms, stack, heap, …) | ao[?] [len] analyze Opcodes (or emulate it) | aO Analyze N instructions in M bytes | ar[?] like ‘dr’ but for the esil vm. (registers) | ap find prelude for current offset | ax[?] manage refs/xrefs (see also afx?) | as[?] [num] analyze syscall using dbg.reg | at[?] [.] analyze execution traces Examples: f ts @ S*~text:0[3]; f t @ section..text f ds @ S*~data:0[3]; f d @ section..data .ad t t+ts @ d:ds |
보통 'aa'를 실행하는 것으로 시작합니다 (analyse all). 이름을 오해의 소지가 있습니다. 왜냐하면 분석할 것이 더 많기 때문입니다. (대부분의 바이너리에서는 'aa' 명령어로 충분합니다. radare2를 -A 플래그와 함께 실행하면 'aaa'를 사용하여 시작 시 바이너리를 곧바로 분석 할 수 있습니다 (예 : r2 -A megabeets_0x1).
[0x08048370]> a? [x] Analyze all flags starting with sym. and entry0 (aa) [0x08048370]> aaa [x] Analyze all flags starting with sym. and entry0 (aa) [x] Analyze len bytes of instructions for references (aar) [x] Analyze function calls (aac) [*] Use -AA or aaaa to perform additional experimental analysis. [x] Constructing a function name for fcn.* and sym.func.* functions (aan) |
Flags
분석 후 radare2는 섹션, 함수, 심볼, 문자열 등과 같은 파일의 흥미로운 오프셋에 이름을 연결합니다. 이러한 이름을 '플래그'라고합니다. 플래그는 '플래그 공간'으로 그룹화 할 수 있습니다. 플래그 공간은 유사한 특성 또는 유형의 플래그에 대한 이름 공간입니다. 플래그 공간을 나열하려면 'fs'를 실행하십시오.
[0x08048370]> fs 0 4 . strings 1 35 . symbols 2 82 . sections 3 5 . relocs 4 5 . imports 5 1 . functions |
'fs <flagspace>'를 사용하여 플래그 공간을 선택하고 'f'를 사용하여 플래그가 출력되도록 할 수 있습니다. 한 줄에 여러 명령을 전달하기 위해 세미콜론 (예 : 'cmd1; cmd2; cmd3; ...')을 사용할 수 있습니다.
[0x08048370]> fs imports; f 0x08048320 6 sym.imp.strcmp 0x08048330 6 sym.imp.strcpy 0x08048340 6 sym.imp.puts 0xffffffff 16 loc.imp.__gmon_start__ 0x08048350 6 sym.imp.__libc_start_main |
잘 알려진 'strcmp', 'strcpy', 'puts'등을 해당 주소와 함께 볼 수 있습니다. 또한 문자열 flagspace를 나열 할 수 있습니다.
[0x08048370]> fs strings; f 0x08048700 21 str._n__.::_Megabeets_::. 0x08048715 23 str.Think_you_can_make_it_ 0x0804872c 10 str.Success__n 0x08048736 22 str.Nop__Wrong_argument._n |
Strings
r2가 일부 오프셋을 문자열, 일종의 변수 이름으로 표시했음을 알 수 있습니다. 이제 문자열 자체를 살펴 보겠습니다. 파일의 문자열을 나열하는 방법에는 여러 가지가 있으며, 가장 적합한 것을 선택해야 합니다.
iz - 데이터 섹션(Data Section)의 문자열 나열
izz - 전체 바이너리에서 문자열 찾기
[0x08048370]> iz vaddr=0x08048700 paddr=0x00000700 ordinal=000 sz=21 len=20 section=.rodata type=ascii string=\n .:: Megabeets ::. vaddr=0x08048715 paddr=0x00000715 ordinal=001 sz=23 len=22 section=.rodata type=ascii string=Think you can make it? vaddr=0x0804872c paddr=0x0000072c ordinal=002 sz=10 len=9 section=.rodata type=ascii string=Success!\n vaddr=0x08048736 paddr=0x00000736 ordinal=003 sz=22 len=21 section=.rodata type=ascii string=Nop, Wrong argument.\n |
우리는 이미 이 문자열의 대부분을 알고 있습니다. 바이너리를 실행할 때 그 문자열을 보았습니다. 기억합니까? 우리는 "Success"문자열을 보지 못했지만, 아마도 이것이 우리의 'good boy'메시지 일 것입니다. 이제 문자열을 얻었으므로 프로그램에서 어디에서 사용했는지 살펴 보겠습니다.
[0x08048370]> axt @@ str.* data 0x8048609 push str._n__.::_Megabeets_::. in main data 0x8048619 push str.Think_you_can_make_it_ in main data 0x8048646 push str._n_tSuccess__n in main data 0x8048658 push str._n_tNop__Wrong_argument._n in main |
이 명령은 우리에게 radare2 기능이 더 많이 있음을 보여줍니다. 'axt'명령은 해당 주소에서 "find data / code references "( 'ax?' 확인)에 사용됩니다. '@@'은 foreach 반복자 기호와 비슷하며 오프셋 목록 ( '@@?' 확인)에 대해 명령을 반복하는 데 사용되며 'str. *'은 'str.'으로 시작하는 모든 플래그의 와일드 카드입니다.
이 조합은 문자열 플래그를 나열하는 것뿐만 아니라 사용 된 함수 이름과 참조 된 명령어를 나열하는 데 도움이됩니다. 문자열을 확인하는 방법은 저번 Flags섹션에서 소개했던 flagspace (기본값은 'fs *')를 선택해야합니다.
Seeking
전에 언급했듯이, 이번에 우리는 프로그램의 진입 점에 있었고, 지금은 나아갈 때입니다. 방금 나열한 문자열은 모두 'main'에 의해 참조됩니다. 오프셋에서 오프셋로 이동하기 위해서는 's'로 표시된 'seek'명령을 사용해야 합니다.
이미 알고 계시 겠지만, 명령어 뒤에 ?를 추가하는 것은 (거의) 모든 문제에 대한 해답입니다.
[0x08048370]> s? |Usage: s # Seek commands | s Print current address | s addr Seek to address | s- Undo seek | s- n Seek n bytes backward | s– Seek blocksize bytes backward | s+ Redo seek | s+ n Seek n bytes forward | s++ Seek blocksize bytes forward | s[j*=] List undo seek history (JSON, =list, *r2) | s/ DATA Search for next occurrence of ‘DATA’ | s/x 9091 Search for next occurrence of \x90\x91 | s.hexoff Seek honoring a base from core->offset | sa [[+-]a] [asz] Seek asz (or bsize) aligned to addr | sb Seek aligned to bb start | sC[?] string Seek to comment matching given string | sf Seek to next function (f->addr+f->size) | sf function Seek to address of specified function | sg/sG Seek begin (sg) or end (sG) of section or file | sl[?] [+-]line Seek to line | sn/sp Seek next/prev scr.nkey | so [N] Seek to N next opcode(s) | sr pc Seek to register |
기본적으로 seek 명령은 주소(address) 또는 수학 표현식(math expression)을 인자로 허용합니다. 표현식은 수학 연산, 플래그 또는 메모리 액세스 연산이 될 수 있습니다. 우리는 main 함수를 찾고 싶습니다. main 함수를 실행하면됩니다. 그러나 먼저 afl 명령 (Analyze Functions List)을 사용하여 radare2가 우리에게 보여주는 다른 함수들을 보도록 합시다.
[0x08048370]> afl 0x080482ec 3 35 sym._init 0x08048320 1 6 sym.imp.strcmp 0x08048330 1 6 sym.imp.strcpy 0x08048340 1 6 sym.imp.puts 0x08048350 1 6 sym.imp.__libc_start_main 0x08048360 1 6 sub.__gmon_start___252_360 0x08048370 1 33 entry0 0x080483a0 1 4 sym.__x86.get_pc_thunk.bx 0x080483b0 4 43 sym.deregister_tm_clones 0x080483e0 4 53 sym.register_tm_clones 0x08048420 3 30 sym.__do_global_dtors_aux 0x08048440 4 43 -> 40 sym.frame_dummy 0x0804846b 19 282 sym.rot13 0x08048585 1 112 sym.beet 0x080485f5 5 127 main 0x08048680 4 93 sym.__libc_csu_init 0x080486e0 1 2 sym.__libc_csu_fini 0x080486e4 1 20 sym._fini |
우리가 전에 보았던 imports, 몇몇 .ctors, entrypotins, libc, main과 'sym.beet'과 'sym.rot13'이라는 두 가지 흥미로운 함수가 있습니다.
Disassembling
main function
이제는 assembly를 볼 시간입니다. 우리는 먼저 s main을 사용하여 함수를 탐색 한 다음 pdf (Print Disassemble Function)를 사용하여 이를 disassemble해야합니다.
프롬프트의 주소가 main의 주소로 변경된 방법에 주의하십시오.
[0x08048370]> s main [0x080485f5]> pdf ;– main: / (fcn) main 127 | main (); | ; var int local_8h @ ebp-0x8 | ; var int local_4h @ esp+0x4 | ; DATA XREF from 0x08048387 (entry0) | 0x080485f5 8d4c2404 lea ecx, [esp + local_4h] ; 0x4 | 0x080485f9 83e4f0 and esp, 0xfffffff0 | 0x080485fc ff71fc push dword [ecx – 4] | 0x080485ff 55 push ebp | 0x08048600 89e5 mov ebp, esp | 0x08048602 53 push ebx | 0x08048603 51 push ecx | 0x08048604 89cb mov ebx, ecx | 0x08048606 83ec0c sub esp, 0xc | 0x08048609 6800870408 push str._n__.::_Megabeets_::. ; str._n__.::_Megabeets_::. | 0x0804860e e82dfdffff call sym.imp.puts ; int puts(const char *s) | 0x08048613 83c410 add esp, 0x10 | 0x08048616 83ec0c sub esp, 0xc | 0x08048619 6815870408 push str.Think_you_can_make_it_ ; “Think you can make it?” @ 0x8048715 | 0x0804861e e81dfdffff call sym.imp.puts ; int puts(const char *s) | 0x08048623 83c410 add esp, 0x10 | 0x08048626 833b01 cmp dword [ebx], 1 ; [0x1:4]=0x1464c45 | ,=< 0x08048629 7e2a jle 0x8048655 | | 0x0804862b 8b4304 mov eax, dword [ebx + 4] ; [0x4:4]=0x10101 | | 0x0804862e 83c004 add eax, 4 | | 0x08048631 8b00 mov eax, dword [eax] | | 0x08048633 83ec0c sub esp, 0xc | | 0x08048636 50 push eax | | 0x08048637 e849ffffff call sym.beet | | 0x0804863c 83c410 add esp, 0x10 | | 0x0804863f 85c0 test eax, eax | ,==< 0x08048641 7412 je 0x8048655 | || 0x08048643 83ec0c sub esp, 0xc | || 0x08048646 682c870408 push str.Success__n ; “Success!.” @ 0x804872c | || 0x0804864b e8f0fcffff call sym.imp.puts ; int puts(const char *s) | || 0x08048650 83c410 add esp, 0x10 | ,===< 0x08048653 eb10 jmp 0x8048665 | ||| ; JMP XREF from 0x08048629 (main) | ||| ; JMP XREF from 0x08048641 (main) | |-> 0x08048655 83ec0c sub esp, 0xc | | 0x08048658 6836870408 push str.Nop__Wrong_argument._n ; “Nop, Wrong argument..” @ 0x8048736 | | 0x0804865d e8defcffff call sym.imp.puts ; int puts(const char *s) | | 0x08048662 83c410 add esp, 0x10 | | ; JMP XREF from 0x08048653 (main) | `—> 0x08048665 b800000000 mov eax, 0 | 0x0804866a 8d65f8 lea esp, [ebp – local_8h] | 0x0804866d 59 pop ecx | 0x0804866e 5b pop ebx | 0x0804866f 5d pop ebp | 0x08048670 8d61fc lea esp, [ecx – 4] \ 0x08048673 c3 ret |
어셈블리를 읽는 것만으로도 우리는 pseudo-code를 생성할 수 있습니다 :
if (argc > 1 && beet(argv[1]) == true) # i.e - if any argument passed to the program AND the result of beet, given the passed argument, is true # argc is the number of arguments passed to the program # argc will be at least 1 becuase the first argument is the program name # argv is the aray of parameters passed to the program { print "success" } else { print "fail" } exit |
Visual Mode & Graph Mode
radare2는 매우 강력하고 효율적인 비주얼 모드 세트를 갖추고 있습니다. 비주얼 모드는 훨씬 사용자에게 친숙하며 r2를 사용하여 새로운 수준으로 되돌릴 수 있습니다. V를 누르면 비쥬얼 모드 화면이 나타납니다.
p/P를 사용하여 모드를 변경하십시오. 화면 맨 위에 뷰를 생성하는 데 사용된 명령을 볼 수 있습니다. p를 사용하여 디스 어셈블리 뷰로 이동합니다.
Basic Visual commands
Movement
k와 j를 사용하여 위아래로 이동할 수 있습니다. jump 또는 call 할 경우 Enter 키를 누르면 목적지 주소로 이동합니다. 각 jump 및 call 옆에 대괄호 안에 숫자가 있습니다. 키보드에서 숫자를 누르면 function / address로 이동합니다.
Help
언제나처럼 radare에서 ?를 입력 시 도움말 화면으로 이동합니다. 비주얼 모드의 명령을 탐색 할 수 있습니다.
Cross-References
x / X를 사용하여 함수의 to / from 참조를 확인할 수 있습니다. 번호를 사용하여 참조로 건너 뜁니다.
radare2 commands
: 사용 시 비쥬얼 모드에서 r2 명령어를 실행할 수 있습니다.
Comment
; [-] 주석을 사용하여 주석을 추가하거나 제거 할 수 있습니다.
Mark
m <key>는 특정 오프셋을 원하는 키로 표시하는 데 사용할 수 있습니다. '<key> 눌러 키로 이동하십시오
Quit
r2 셸로 돌아가려면 q를 누릅니다.
Visual Graphs
다른 디스어셈블러와 마찬가지로 radare2에서도 그래프 보기가 있습니다. VV를 실행하고 h / j / k / l을 사용하여 왼쪽 / 아래 / 위로 / 오른쪽으로 이동하고 jump call(e.g gd) 옆에 있는 g와 key를 이용해 함수로 점프하여 비주얼 그래프 모드에 액세스 할 수 있습니다.
Disassembling 'beet'
디스어셈블링 후 다시 돌아가서 beet 함수를 확인해 봅시다. 이전에 보았듯이 바이너리는 프로그램에 전달한 인자로 호출 된 beet 결과를 검사합니다. 우리는 몇가지 방법으로 beet를 출력할 수 있으며 몇가지 방법을 알아도록 합시다.
- r2쉘에서 beet함수를 Seek하고 s sym.beet명령어를 이용해 함수를 출력하십시오. (sym.beet는 beet 함수의 플래그를 의미합니다. f sym.<tab>을 실행하여 'sym' 플래그를 프린트 한 다음 pdf명령어를 실행시키십시오.
- pdf @sym.beet을 사용하여 r2 쉘에서 출력하십시오. @sms 임시 seeking으로 사용됩니다. 즉 "offset sym.beet에서 함수를 출력하세요.
- 3(jump call 옆의 숫자)를 사용하여 main의 Visual Mode에서 beet함수로 점프하세요.
- gd('d'는 jump call 옆의 문자)를 사용하여 main의 Visual Mode에서 beet 함수로 점프하세요.
다음은 비주얼 그래프 모드에서의 비트 (beet) 모양입니다.
주어진 인자가 버퍼(buffer)에 복사되는 것을 볼 수 있습니다. 버퍼는 ebp - local_88h에 있습니다. 'local_88h'는 실제로 0x88이며 십진수는 136입니다. 우리는 이것을 실행하여 볼 수 있습니까? 0x88. Visual Graph 모드에서 r2 명령을 실행하려면 : 를 사용하여 명령어를 : 뒤에다가 입력하세요.
:> ? 0x88 136 0x88 0210 136 0000:0088 136 “\x88” 10001000 136.0 136.000000f 136.000000 |
따라서 128 바이트가 스택의 버퍼에 할당되고 다음 4 바이트는 이전 스택 프레임의 저장된 ebp 포인터가되며 다음 4 바이트는 반환 주소가되며 최대 136 바이트가 됩니다. 버퍼가 주어진 인자로 채워지면 sym.rot13이라는 함수의 결과와 비교됩니다.
Rot-13은 CTF와 Crackmes에서 많이 사용되는 유명한 대체 암호(Substitution Cipher)입니다. 이 함수는 radare가 문자열로 인식되지 않는 것처럼 보이는 9 개의 16 진수 값으로 호출됩니다. 이 주소에서 ahi를 사용하여 수동으로 처리할 수 있습니다.
:> ahi s @@=0x080485a3 0x080485ad 0x080485b7 |
ahi는 특정 오프셋을 문자열로 설정하는데 사용됩니다. (ahi ? 참고). @@은 반복자 (@@? 참고)이고 주소는 sym.beet의 주소이며 radare2는 포함된 문자열로 식별되지 않습니다. 명령을 실행한 후 그래프가 새로고침됩니다 (그렇지 않은 경우 r 사용). 그러면 다음과 같이 보입니다.
Great! 숨어 있던 문자열이 "Megabeets"인 것처럼 보입니다 (endianness 때문에 역순으로 PUSH됩니다).
바이너리는 "Megabeets"에서 rot13을 수행한 다음 결과를 strcmp를 사용하여 전달한 인자와 비교합니다. 다행히 r2 프레임워크는 이미 rahash2 유틸리티에 rot13 암호를 포함하고 있습니다.
rahash2는 다양한 알고리즘을 사용하여 파일이나 문자열의 체크섬을 계산합니다.
자세한 정보는 man rahash2를 확인하십시오.
:> !rahash2 -E rot -S s:13 -s ‘Megabeets\n’ Zrtnorrgf |
rahash2는 rot13 ( "Megabeets")을 수행한 결과 "Zrtnorrgf"를 출력했습니다. ! 명령어를 사용하여 system (3)에서와 같이 r2 쉘에서 쉘 명령을 실행할 수 있습니다. 우리는 "Zrtnorrgf"가 우리의 입력과 비교된다고 가정할 수 있습니다.
디버그 모드에서 ood (check ood?)를 사용하여 "Zrtnorrgf"를 인자로 입력한 다음 바이너리를 열고 우리가 얻을 수있는 것을 살펴 보겠습니다.
[0xf7749be9]> ood? | ood [args] reopen in debugger mode (with args) [0xf7749be9]> ood Zrtnorrgf Wait event received by different pid 7415 Wait event received by different pid 7444 Process with PID 7575 started… File dbg:///home/remnux/Desktop/tutorials/megabeets_0x1 Zrtnorrgf reopened in read-write mode = attach 7575 7575 Assuming filepath /home/remnux/Desktop/tutorials/megabeets_0x1 [0xf7749be9]> dc Selecting and continuing: 7575.:: Megabeets ::. Think you can make it? Success!PTRACE_EVENT_EXIT pid=7575, status=0x0 |
Woohoo! 우리는 Success 메시지를 확인했으며 crame me 문제를 풀었습니다.