[드림핵] rev-basic-3 풀이

비얌·2025년 1월 27일
0

리버싱

목록 보기
19/24
post-thumbnail

개요

예전에 rev-basic-2까지 풀었길래 이번 포스팅에서는 이어서 3을 풀어보려고 한다.

파일 실행해보기

아무거나 입력해봤을 때, Wrong이라는 문구가 나오는 걸 확인할 수 있었다.

정적 분석하기

  1. Search > For Strings에서 Wrong을 찾아갔다.

  2. 어셈블리와 디컴파일된 전체 코드를 확인할 수 있었다.
    처음 열었을 때 나름 분석해서 주석으로 남겨보았지만 일단 어셈블리는 거의 이해할 수 없었다.

  3. 디컴파일된 전체 코드는 아래와 같다.
    주석을 읽어보면 짐작한 내용이 엉망일 것 같은데🤣 일단 남겨본다..

void FUN_140001120(undefined8 param_1,undefined8 param_2,undefined8 param_3,undefined8 param_4)

{
  undefined8 uVar1;
  longlong lVar2;
  undefined *puVar3;
  undefined auStack312 [32];
  undefined local_118 [256];
  ulonglong local_18;
  
                    /* 무슨 뜻인지 모르겠음. 5바이트랑 256바이트랑 XOR연산을
                       한다? 왜인지 결과는 0일 것 같음 */
  local_18 = DAT_140003028 ^ (ulonglong)auStack312;
                    /* 왜냐면 보통 변수를 0으로 초기화하는 것 같아서... */
  puVar3 = local_118;
                    /* 256만큼 puVar3을 증가시키므로 puVar3는 256 아닐까? */
  for (lVar2 = 256; lVar2 != 0; lVar2 = lVar2 + -1) {
    *puVar3 = 0;
    puVar3 = puVar3 + 1;
  }
                    /* 아마 scanf 함수. 3개의 인자를 받는다.  */
  FUN_1400011b0("Input : ",param_2,param_3,param_4);
                    /* 분석 안해도 될 것 같음 */
  FUN_140001210("%256s",local_118,param_3,param_4);
                    /* 분석해야 할 함수. 지역변수 local_118을 인자로 넣어서
                       결과가 0이 아닌 값이 리턴되어야 한다.
                       결국 local_118을 구해야 하는 것 같다. */
  uVar1 = FUN_140001000((longlong)local_118);
  if ((int)uVar1 == 0) {
    puts("Wrong");
  }
  else {
    puts("Correct");
  }
  FUN_140001300(local_18 ^ (ulonglong)auStack312);
  return;
}

위의 전체 코드에서 아래의 부분을 발췌해서 보면 uVar1이라는 변수가 0이 아니어야 Correct를 출력하는 것을 알 수 있었다.

즉 local_118이라는 인자를 FUN_140001000 함수에 넣었을 때, 0이 아닌 값이 리턴되어야 한다. FUN_140001000에 들어가보았다.

uVar1 = FUN_140001000((longlong)local_118);
  if ((int)uVar1 == 0) {
    puts("Wrong");
  }
  else {
    puts("Correct");
  }
  1. FUN_140001000 해석하기
    FUN_140001000는 아래 이미지와 같이 디컴파일 되어있었다.
undefined8 FUN_140001000(longlong param_1_local_118)

{
  uint local_18;
  
  local_18 = 0;
  while( true ) {
    if (23 < local_18) {
      return 1;
    }
                    /* break되면 0이 리턴되므로 이 if문이 false가 되어야 함. -> 즉, 전향==후항. param_1을 param_1_local_118로  변수명 변경해주었다. */
    if ((uint)(byte)(&DAT_140003000)[(int)local_18] !=
        (*(byte *)(param_1_local_118 + (int)local_18) ^ local_18) + local_18 * 2) break;
    local_18 = local_18 + 1;
  }
  return 0;
}

while문이 break되면 0이 반환되므로, 아래 식의 전항과 후항이 동일해야 한다(그래야 식이 false가 되어 break되지 않음)

((uint)(byte)(&DAT_140003000)[(int)local_18] !=
(*(byte *)(param_1_local_118 + (int)local_18) ^ local_18) + local_18 * 2

일단 local_18는 0부터 1식 증가하므로 인덱스를 의미하는 것 같다는 생각을 했다.

그러면 (&DAT_140003000)[(int)local_18]는 문자열을 한글자씩 가져온다는 것 같다. DAT_140003000 부분을 더블클릭하여 들어가보았다.

16진수로 24개~의 글자가 있는 걸 확인했다. 아마 위의 식을 거치면 이 문자들이 변환되어 합쳐진 문자열이 답이 될 것이다.

  1. FUN_140001000 코드 짜기
    23글자를 하나하나 구할 수 없다고 생각해서 자바스크립트로 코드를 짜서 구했다.

a ^ b = c는 a ^ c = b와 같음

// 16진수이며, 어셈블리 코드에서 긁어왔다. 
const array = [
  '49',
  '60',
  '67',
  '74',
  '63',
  '67',
  '42',
  '66',
  '80',
  '78',
  '69',
  '69',
  '7B',
  '99',
  '6D',
  '88',
  '68',
  '94',
  '9F',
  '8D',
  '4D',
  'A5',
  '9D',
  '45',
];

// 최종적으로 구할 문자열을 담을 result 배열. 
// character라는 변수 선언
let result = [];
let character;

// 16진수를 10진수로 변환
for (let i = 0; i < array.length; i++) {
  array_i = parseInt(array[i], 16);
 
// XOR의 특징을 이용한 역연산.
// 💥 왜 (array_i - i * 2) ^ i - i이 아닌지 모르겠다. 디컴파일된 코드를 보면 (byte *)(character + (int)local_18)이니까 local_18을 빼줘야 하는데.. character 인덴트를 말하는 것 같은데 왜인지 잘 모르겠다. 메모리와 포인터 개념을 공부해야 할 듯 싶다. 
  character = (array_i - i * 2) ^ i
  result.push(String.fromCharCode(character));
}

// I_am_X0_xo_Xor_eXcit1ng
console.log(result.join(''));

결과

I_am_X0_xo_Xor_eXcit1ng를 입력하여 Correct를 출력하는 데 성공했다!

profile
🐹강화하고 싶은 기억을 기록하고 공유하자🐹

0개의 댓글

관련 채용 정보