Hacking/Reversing

Reversing@radare2# A journey into Radare 2 – Part 1: Simple crackme

hi0802 2017. 10. 11. 22:32

https://www.megabeets.net/a-journey-into-radare-2-part-1/


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
-'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/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 문제를 풀었습니다.