rev 2번째 문제이고 자만하면 안되지만 풀었다는 것에 꽤 성장이 있었음을 느끼는 문제이다.
( 사실 그래도 진짜 쉬운 문제에 속한다. 슬럼프라 좋게 생각해도 될 것 같다. )
사실 문제 이름과 설명에서는 전혀 추론을 못했고 바이너리 파일이 주어져서 ida로 열어보았다.
int __cdecl main(int argc, const char **argv, const char **envp)
{
const char **num; // rdx
char input_buf; // bl
int flag_buf; // er12
const char **n_plus; // rdx
int v8; // eax
const char **v9; // rdx
int v10; // eax
char input[64]; // [rsp+10h] [rbp-F0h] BYREF
int flag[31]; // [rsp+50h] [rbp-B0h]
_DWORD main_i[3]; // [rsp+CCh] [rbp-34h] BYREF
int param1; // [rsp+D8h] [rbp-28h]
_DWORD i[4]; // [rsp+DCh] [rbp-24h] BYREF
int v16; // [rsp+ECh] [rbp-14h]
if ( argc != 1 )
{
if ( argc == 3 )
{
param1 = *(_DWORD *)*argv;
if ( !param1 || param1 == 1 )
return 1;
*(_QWORD *)&main_i[1] = malloc(4uLL);
main_i[0] = param1 - 1;
n_plus = (const char **)(unsigned int)(param1 - 1);
**(_DWORD **)&main_i[1] = (_DWORD)n_plus;
v16 = 0;
v8 = main(3, (const char **)&main_i[1], n_plus);
v16 += v8;
v9 = (const char **)--main_i[0];
**(_DWORD **)&main_i[1] = main_i[0];
v10 = main(3, (const char **)&main_i[1], v9);
return v10 + v16;
}
LABEL_13:
puts("Wrong...\n");
return -1;
}
puts("Guess my flag!!\n");
fgets(input, 49, _bss_start);
if ( strlen(input) <= 0x1D )
goto LABEL_13;
*(_QWORD *)&i[1] = malloc(4uLL);
flag[0] = 117;
flag[1] = 107;
flag[2] = 97;
flag[3] = 119;
flag[4] = 99;
flag[5] = 115;
flag[6] = 122;
flag[7] = 97;
flag[8] = 15;
flag[9] = 67;
flag[10] = 49;
flag[11] = 245;
flag[12] = 196;
flag[13] = 269;
flag[14] = 533;
flag[15] = 948;
flag[16] = 1618;
flag[17] = 2679;
flag[18] = 4154;
flag[19] = 6658;
flag[20] = 10915;
flag[21] = 17756;
flag[22] = 28613;
flag[23] = 46360;
flag[24] = 75060;
flag[25] = 121457;
flag[26] = 196390;
flag[27] = 317717;
flag[28] = 514246;
flag[29] = 832085;
for ( i[0] = 0; i[0] <= 29; ++i[0] )
{
num = (const char **)i[0];
**(_DWORD **)&i[1] = i[0];
input_buf = input[i[0]];
flag_buf = flag[i[0]];
if ( input_buf != ((unsigned __int8)flag_buf ^ (unsigned __int8)main(3, (const char **)&i[1], num)) )
goto LABEL_13;
}
puts("Nice!!!");
return 1;
}
main 코드는 위와 같다. 소스 값들을 주고 역계산을 하는 전형적인 쉬운 문제이지만 처음 풀어보는 매커니즘인 것 같다.
문자열 길이나 if문 경계 값을 봐도 flag의 길이가 30인 것은 바로 알 수 있다.
for ( i[0] = 0; i[0] <= 29; ++i[0] )
{
num = (const char **)i[0];
**(_DWORD **)&i[1] = i[0];
input_buf = input[i[0]];
flag_buf = flag[i[0]];
if ( input_buf != ((unsigned __int8)flag_buf ^ (unsigned __int8)main(3, (const char **)&i[1], num)) )
goto LABEL_13;
}
이후 이 for문 부터가 중요해지는데 i[0]를 보고 헷갈렸는데 사실은 배열이 아닌 별의미 없는 반복 index로 활용한다.
input 문자열과 flag 문자열에 어떤 값을 xor한 값을 하나씩 확인하며 다를 경우 goto LABEL_13으로 fail 부분으로 보내버린다. ( 참고로 LABEL_13 이렇게 코드 앞에 있어도 goto문이 아니면 실행이 안된다. )
결국 어떤 값은 따지고 보면 main(3, index, index)이다.
if ( argc != 1 )
{
if ( argc == 3 )
{
param1 = *(_DWORD *)*argv;
if ( !param1 || param1 == 1 )
return 1;
*(_QWORD *)&main_i[1] = malloc(4uLL);
main_i[0] = param1 - 1;
n_plus = (const char **)(unsigned int)(param1 - 1);
**(_DWORD **)&main_i[1] = (_DWORD)n_plus;
v16 = 0;
v8 = main(3, (const char **)&main_i[1], n_plus);
v16 += v8;
v9 = (const char **)--main_i[0];
**(_DWORD **)&main_i[1] = main_i[0];
v10 = main(3, (const char **)&main_i[1], v9);
return v10 + v16;
}
argc에 3을 쥐어주고 main을 돌리기 때문에 if문 안쪽 계산까지 실행되는 것을 알 수 있다.
if문 안쪽 계산을 결국 따지고 보면 main(3, a, b)일 때, a가 0,1일 경우 1을 반환하고 아닐 경우는
main(3, a-1, b-1), main(3, a-2, b-2) 이렇게 재귀호출을 두 번씩 하는 것을 볼 수 있다.
argc를 경계로 값 생성을 위해 main을 반복시키는 구조이다. 곱씹을수록 재밌는 구조인 것 같다.
#include <stdio.h>
int func(int num1, int num2){
if(num1 == 0 || num1 == 1)
return 1;
else{
int n1 = func(num1-1, num2-1);
int n2 = func(num1-2, num2-1);
return n1+n2;
}
}
int main(){
int flag[30]={ 117, 107, 97, 119, 99, 115, 122, 97, 15, 67, 49, 245, 196, 269, 533,
948, 1618, 2679, 4154, 6658, 10915, 17756, 28613, 46360, 75060, 121457, 196390,
317717, 514246, 832085 };
for(int i=0; i<30; i++){
printf("%c", flag[i]^func(i,i));
}
}
파악한 매커니즘을 거꾸로 이용하는 decrypt를 c코드로 짜봤다. 풀었을 때는 뿌듯했는데 막상 보니까 짧아서 엄청 쉬웠던 문제인가 싶기도 하다...
매커니즘은 파악했는데 또 decrypt 코드가 작동 안되서 못 풀 수도 있다고 생각했는데 1트만에 되서 놀랐다. 어쨋든 기분은 좋다.
'security > rev' 카테고리의 다른 글
[TJCTF 2024] guess-what (0) | 2024.05.18 |
---|---|
[MireaCTF] c1ng3 checker 풀이 (0) | 2024.05.01 |
[Dreamhack] ez_rev 풀이 (0) | 2024.04.30 |
[Dreamhack] rev-basic-3 (0) | 2024.04.30 |
[Dreamhack] rev-basic-2 (0) | 2024.04.30 |