

[ida에서 디컴파일한 결과]
int __fastcall main(int argc, const char **argv, const char **envp)
{
__int64 src[10]; // [rsp+0h] [rbp-F0h] BYREF
char dest[72]; // [rsp+A0h] [rbp-50h] BYREF
unsigned int i; // [rsp+ECh] [rbp-4h]
puts("---Counter---");
for ( i = 10; (int)i > 0; --i )
{
printf("%d\n", i);
if ( i == 3 )
{
strcpy((char *)src, "IM{508889j32j87j9jg54650840428hjhi2ii08h74ihj538h543j7g6k5jk8jih22f}");
memcpy(dest, src, 0x45uLL);
}
}
puts("---END---");
return 0;
}
memcpy(복사할 구조체 주소, 원본 구조체 주소, 크기) : 정해진 길이만큼 복사. 주로 이진 데이터는 이 함수를 이용하여 처리
strcpy(char str1, const char str2) : str2의 내용을 sr1에 문자열 복사 (\0 만나면 여기까지 복사되고 종료)
-> 10부터 1까지 카운터를 세는데, 만약 i가 3일 경우 strcpy 함수가 사용되면서 위에 있는 지정 문자열을 src 변수에 이를 저장하고, memcpy 함수가 사용되면서 0x45uLL만큼 구조체를 복사한다.

그래프로 확인해보니,

조건에 따라 성공거나 '---END---'가 출력되면서 끝나는 로직이라는 걸 볼 수 있다.
따라서 여기서 중요한 부분은 바로
cmp [rbp+var_4], 5
jnz short loc_15FE
이다.
jnz(Not Zero Jump) : 비교값이 0이 아닌 경우 A로 이동. 비교값이 0인 경우 점프하지 않고 다음 코드 실행
-> [rbp+var_4]가 5이면 flag_gen을 호출한다.
[flag_gen함수 디컴파일 결과]
_BYTE *__fastcall flag_gen(const char *a1, __int64 a2, int a3)
{
size_t v3; // rbx
_BYTE *result; // rax
char v6[64]; // [rsp+20h] [rbp-A0h]
char v7[32]; // [rsp+60h] [rbp-60h] BYREF
char v8[35]; // [rsp+80h] [rbp-40h] BYREF
char v9; // [rsp+A3h] [rbp-1Dh]
int j; // [rsp+A4h] [rbp-1Ch]
int i; // [rsp+A8h] [rbp-18h]
int v12; // [rsp+ACh] [rbp-14h]
strcpy(v8, "abcdefghijklmnopqrstuvwxyz");
strcpy(v7, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
for ( i = 0; i <= 25; ++i )
{
v6[(a3 + i) % 26 + 32] = v8[i];
v6[(a3 + i) % 26] = v7[i];
}
v6[i + 32] = 0;
v6[i] = 0;
for ( j = 0; ; ++j )
{
v3 = j;
if ( v3 >= strlen(a1) )
break;
v9 = a1[j];
if ( ((*__ctype_b_loc())[v9] & 0x200) != 0 )
{
*(_BYTE *)(a2 + j) = v6[v9 - 65];
}
else if ( ((*__ctype_b_loc())[v9] & 0x100) != 0 )
{
*(_BYTE *)(a2 + j) = v6[v9 - 65];
}
else if ( ((*__ctype_b_loc())[v9] & 0x800) != 0 )
{
v12 = (a3 + 3) * v9 % 9;
if ( v12 <= 7 || v12 > 9 )
v12 += 50;
else
v12 += 40;
*(_BYTE *)(j + a2) = v12;
}
else
{
*(_BYTE *)(a2 + j) = v9;
}
}
result = (_BYTE *)(a2 + strlen(a1));
*result = 0;
return result;
}
... 뭔가 잘하면 여기서 해결할 수 있을 것 같지만, 크게 감이 안오니 gdb로 살펴봐야겠다..
0x0000000000001494 <+0>: endbr64
0x0000000000001498 <+4>: push rbp
0x0000000000001499 <+5>: mov rbp,rsp
0x000000000000149c <+8>: sub rsp,0xf0
0x00000000000014a3 <+15>: mov DWORD PTR [rbp-0x4],0x0
0x00000000000014aa <+22>: lea rax,[rip+0xb53] # 0x2004
0x00000000000014b1 <+29>: mov rdi,rax
0x00000000000014b4 <+32>: call 0x1090 <puts@plt>
0x00000000000014b9 <+37>: mov DWORD PTR [rbp-0x4],0xa
0x00000000000014c0 <+44>: jmp 0x15a0 <main+268>
0x00000000000014c5 <+49>: mov eax,DWORD PTR [rbp-0x4]
0x00000000000014c8 <+52>: mov esi,eax
0x00000000000014ca <+54>: lea rax,[rip+0xb41] # 0x2012
0x00000000000014d1 <+61>: mov rdi,rax
0x00000000000014d4 <+64>: mov eax,0x0
0x00000000000014d9 <+69>: call 0x10b0 <printf@plt>
0x00000000000014de <+74>: cmp DWORD PTR [rbp-0x4],0x3
0x00000000000014e2 <+78>: jne 0x159c <main+264>
0x00000000000014e8 <+84>: movabs rax,0x38383830357b4d49
0x00000000000014f2 <+94>: movabs rdx,0x6a37386a32336a39
0x00000000000014fc <+104>: mov QWORD PTR [rbp-0xf0],rax
0x0000000000001503 <+111>: mov QWORD PTR [rbp-0xe8],rdx
0x000000000000150a <+118>: movabs rax,0x3035363435676a39
0x0000000000001514 <+128>: movabs rdx,0x6a68383234303438
0x000000000000151e <+138>: mov QWORD PTR [rbp-0xe0],rax
0x0000000000001525 <+145>: mov QWORD PTR [rbp-0xd8],rdx
0x000000000000152c <+152>: movabs rax,0x6838306969326968
0x0000000000001536 <+162>: movabs rdx,0x3833356a68693437
0x0000000000001540 <+172>: mov QWORD PTR [rbp-0xd0],rax
0x0000000000001547 <+179>: mov QWORD PTR [rbp-0xc8],rdx
0x000000000000154e <+186>: movabs rax,0x3667376a33343568
0x0000000000001558 <+196>: movabs rdx,0x68696a386b6a356b
0x0000000000001562 <+206>: mov QWORD PTR [rbp-0xc0],rax
0x0000000000001569 <+213>: mov QWORD PTR [rbp-0xb8],rdx
0x0000000000001570 <+220>: mov DWORD PTR [rbp-0xb0],0x7d663232
0x000000000000157a <+230>: mov BYTE PTR [rbp-0xac],0x0
0x0000000000001581 <+237>: lea rcx,[rbp-0xf0]
0x0000000000001588 <+244>: lea rax,[rbp-0x50]
0x000000000000158c <+248>: mov edx,0x45
0x0000000000001591 <+253>: mov rsi,rcx
0x0000000000001594 <+256>: mov rdi,rax
0x0000000000001597 <+259>: call 0x10c0 <memcpy@plt>
0x000000000000159c <+264>: sub DWORD PTR [rbp-0x4],0x1
0x00000000000015a0 <+268>: cmp DWORD PTR [rbp-0x4],0x0
0x00000000000015a4 <+272>: jg 0x14c5 <main+49>
0x00000000000015aa <+278>: cmp DWORD PTR [rbp-0x4],0x5
0x00000000000015ae <+282>: jne 0x15fe <main+362>
0x00000000000015b0 <+284>: lea rax,[rip+0xa5f] # 0x2016
0x00000000000015b7 <+291>: mov rdi,rax
0x00000000000015ba <+294>: call 0x1090 <puts@plt>
0x00000000000015bf <+299>: mov eax,DWORD PTR [rbp-0x4]
0x00000000000015c2 <+302>: mov DWORD PTR [rbp-0x8],eax
0x00000000000015c5 <+305>: mov edx,DWORD PTR [rbp-0x8]
0x00000000000015c8 <+308>: lea rcx,[rbp-0xa0]
0x00000000000015cf <+315>: lea rax,[rbp-0x50]
0x00000000000015d3 <+319>: mov rsi,rcx
0x00000000000015d6 <+322>: mov rdi,rax
0x00000000000015d9 <+325>: call 0x11c9 <flag_gen>
0x00000000000015de <+330>: lea rax,[rbp-0xa0]
0x00000000000015e5 <+337>: mov rsi,rax
0x00000000000015e8 <+340>: lea rax,[rip+0xa2d] # 0x201c
0x00000000000015ef <+347>: mov rdi,rax
0x00000000000015f2 <+350>: mov eax,0x0
0x00000000000015f7 <+355>: call 0x10b0 <printf@plt>
0x00000000000015fc <+360>: jmp 0x160d <main+377>
0x00000000000015fe <+362>: lea rax,[rip+0xa1c] # 0x2021
0x0000000000001605 <+369>: mov rdi,rax
0x0000000000001608 <+372>: call 0x1090 <puts@plt>
0x000000000000160d <+377>: mov eax,0x0
0x0000000000001612 <+382>: leave
0x0000000000001613 <+383>: ret
End of assembler dump.
ida에서 발견했던 것이 gdb에 있는지 확인한 후에 해당 주소에 브레이크를 걸어줘야 한다.

break 후 실행(r)

실행하면 내가 break를 걸어둔 위치까지 실행이 된 상태로 멈춰있는 것을 확인해볼 수 있다. 여기서 jnz의 조건을 맞춰주어 flag 값을 알아내야하기 때문에 [rbp-4]의 주소값을 확인한 후 주소값에 조건에 맞는 값을 삽입한 후 실행하면 된다.


플래그값이 나왔다.