해당 바이너리와 같은 경우 핵스레이가 안되서 gdb로 디버깅하면서 코드 분석을 했다.
먼저 objudmp로 대충 봐보면 섹션이 하나밖에 없고 크기가 작아서 분석하는데 어려움은 없었다.
0000000000400080 <.text>:
400080: eb 50 jmp 0x4000d2
400082: 48 31 c0 xor rax,rax
400085: fe c0 inc al
400087: 48 31 ff xor rdi,rdi
40008a: 48 ff c7 inc rdi
40008d: 5e pop rsi
40008e: b2 2e mov dl,0x2e
400090: 0f 05 syscall
400092: 2c 2e sub al,0x2e
400094: ff cf dec edi
400096: 0f 05 syscall
400098: 48 0f b6 7e 01 movzx rdi,BYTE PTR [rsi+0x1]
40009d: 48 31 3e xor QWORD PTR [rsi],rdi
4000a0: 48 ff c6 inc rsi
4000a3: 48 ff ca dec rdx
4000a6: 75 f0 jne 0x400098 ; 반복문 end
4000a8: 83 e1 2e and ecx,0x2e
4000ab: 80 c1 26 add cl,0x26
4000ae: 48 8d 7e 07 lea rdi,[rsi+0x7]
4000b2: 48 8d 77 cb lea rsi,[rdi-0x35]
4000b6: f3 a6 repz cmps BYTE PTR ds:[rsi],BYTE PTR es:[rdi]
4000b8: 48 85 c9 test rcx,rcx
4000bb: 75 49 jne 0x400106
4000bd: 34 2f xor al,0x2f
4000bf: 68 59 61 79 21 push 0x21796159
4000c4: 48 89 e6 mov rsi,rsp
4000c7: b2 04 mov dl,0x4
4000c9: bf 01 00 00 00 mov edi,0x1
4000ce: 0f 05 syscall
4000d0: eb 34 jmp 0x400106
4000d2: e8 ab ff ff ff call 0x400082
4000d7: 57 push rdi
4000d8: 65 6c gs ins BYTE PTR es:[rdi],dx
4000da: 63 6f 6d movsxd ebp,DWORD PTR [rdi+0x6d]
4000dd: 65 20 74 6f 20 and BYTE PTR gs:[rdi+rbp*2+0x20],dh
4000e2: 74 68 je 0x40014c
4000e4: 69 73 20 43 68 61 6c imul esi,DWORD PTR [rbx+0x20],0x6c616843
4000eb: 6c ins BYTE PTR es:[rdi],dx
4000ec: 21 20 and DWORD PTR [rax],esp
4000ee: 0a 45 6e or al,BYTE PTR [rbp+0x6e]
4000f1: 74 65 je 0x400158
4000f3: 72 20 jb 0x400115
4000f5: 74 68 je 0x40015f
4000f7: 65 20 4b 65 and BYTE PTR gs:[rbx+0x65],cl
4000fb: 79 20 jns 0x40011d
4000fd: 74 6f je 0x40016e
4000ff: 20 77 69 and BYTE PTR [rdi+0x69],dh
400102: 6e outs dx,BYTE PTR ds:[rsi]
400103: 3a 20 cmp ah,BYTE PTR [rax]
400105: 00 31 add BYTE PTR [rcx],dh
400107: c0 (bad)
400108: b0 3c mov al,0x3c
40010a: 0f 05 syscall
40010c: 0a 0d 06 1c 22 38 or cl,BYTE PTR [rip+0x38221c06] # 0x38621d18
400112: 18 26 sbb BYTE PTR [rsi],ah
400114: 36 0f 39 ss (bad)
400117: 2b 1c 59 sub ebx,DWORD PTR [rcx+rbx*2]
40011a: 42 2c 36 rex.X sub al,0x36
40011d: 1a 2c 26 sbb ch,BYTE PTR [rsi+riz*1]
400120: 1c 17 sbb al,0x17
400122: 2d 39 57 43 01 sub eax,0x1435739
400127: 07 (bad)
400128: 2b 38 sub edi,DWORD PTR [rax]
40012a: 09 07 or DWORD PTR [rdi],eax
40012c: 1a 01 sbb al,BYTE PTR [rcx]
40012e: 17 (bad)
40012f: 13 13 adc edx,DWORD PTR [rbx]
400131: 17 (bad)
400132: 2d 39 0a 0d 06 sub eax,0x60d0a39
400137: 46 5c rex.RX pop rsp
400139: 7d .byte 0x7d
전체적인 흐름은 syscall을 통해 입력 값을 받고 input[i]^input[i+1] 연산을 총 46회 반복한다.
이 값이 특정영역의 값과 같으면 되는데 특정영역의 값은 repz cmps BYTE PTR ds:[rsi],BYTE PTR es:[rdi] 요 명령어 실행 시 rdi 레지스터에 존재하는 주소에서 46 byte 값을 써주면 된다.
로직이 간단해서 코드 짜는건 간단했고 z3로 돌려주니 flag가 나왔다.
from z3 import *
result = [0x0a,0x0d,0x06,0x1c,0x22,0x38,0x18,0x26,0x36,0x0f,0x39,0x2b,0x1c,0x59,0x42,0x2c,0x36,0x1a,0x2c,0x26,0x1c,0x17,0x2d,0x39,0x57,0x43,0x01,0x07,0x2b,0x38,0x09,0x07,0x1a,0x01,0x17,0x13,0x13,0x17,0x2d,0x39,0x0a,0x0d,0x06,0x46,0x5c,0x7d,0x00]
a = []
s = Solver()
for i in range(0,47):
a.append(BitVec('a['+str(i)+']',8))
for i in range(0,46):
s.add( (a[i]^a[i+1])==result[i])
flag = ""
while s.check() == z3.sat:
flag = ""
m = s.model()
for i in range(0, 47):
flag += chr(int(str(m[a[i]])))
if "flag" in flag:
print flag
for i in range(0,47):
s.add(m[a[i]]!=a[i])
Flag = flag{Yay_if_th1s_is_yer_f1rst_gnisrever_flag!}