[드림핵] Simple Crack Me 2 풀이

비얌·2025년 2월 2일
0

리버싱

목록 보기
22/24
post-thumbnail

개요

저번에 푼 simple_crack_me에 이어서 Simple Crack Me 2도 풀어보려고 한다.

처음에 혼자서는 못풀다가, Ghidra 강의를 수강하고 풀 수 있었다.

문제 : https://dreamhack.io/wargame/challenges/668

파일 실행해보기

파일이 ELF 형식이어서 맥에서도, 윈도우에서도 실행되지 않았다.
그래서 OrbStack이라는 프로그램에서 우분투 VM을 만들었다. apple로 만들면 동작하지 않아 intel로 설정하여 만들었다.

여러 글자를 쳐봤는데, your input length is wrong x(라는 답변이 뜬다.

정적 분석하기 -FUN_00401390

아까 프로그램을 실행했을 때 본 your input length is wrong x( 문자열을 찾아 들어가보았다.

FUN_00401390라는 함수를 발견할 수 있었다.

16진수로 된 값을 보기 쉽게 10진수로 바꿨다. 단축키를 이용하거나 마우스 우클릭하여 변경할 수 있다.

여기서 local_118은 사용자의 입력값이라는 것을 알 수 있는데, 아래 함수에서 추측할 수 있다.
__isoc99_scanf(&DAT_00402045,local_118);

FUN_004011b6 함수 분석하기

가장 윗쪽에 있는 함수인 FUN_004011b6 함수를 더블클릭하여 들어가보았다.

여기서 param_1은 input이므로 input으로 바꿔준다.
그리고 for문을 보면 문자열의 끝인 널문자를 만날 때까지 반복하며 local_18을 1씩 증가시키므로, 문자열의 길이를 리턴하는 함수임을 알 수 있다.

그래서 long FUN_004011b6(char *param1)을 우클릭 > Edit Function Signiture 클릭 후 size_t GetStrLen(char *input)으로 바꿔준다.

그리고 다시 돌아가보면, 입력한 문자열의 길이가 32이여야 Correctyour input is wrong x(이든에 도달할 수 있다는 걸 알게 되었다.

FUN_004011ef 함수 분석하기

그 다음 함수인 FUN_004011ef 함수에 들어가보았다.

param2와 xor하는 함수임을 알 수 있었다.

  1. 함수 이름을 XORWithParam2로 바꾸어주었다.
  2. param_1은 사실 문자열 input이므로 자료형과 이름을 고쳐주었다.
  3. sVar1도 문자열의 길이를 리턴하는 함수의 반환값이므로 len으로 변수명을 변경해주었다.
  4. 읽기 쉽도록 0x20(16진수)을 32(10진수)로 변환했다.

그럼 아래와 같이 더 가독성 좋은 함수가 된다.

FUN_00401263 함수 분석하기

그 다음 함수인 FUN_00401263에 들어가보았다.

문자열을param2만큼 증가시킨 input을 리턴하는 함수임을 알 수 있었다.

  1. 함수명을 INCWithParam2로 바꾸어주었다.
  2. param_1은 사실 문자열 input이므로 자료형과 이름을 고쳐주었다.
  3. 읽기 쉽도록 0x20(16진수)을 32(10진수)로 변환했다.

FUN_004012b0 함수 분석하기

INCWithParam2와 동일한데 param2만큼 증가시키는 것이 아니라 감소시킨다.

INCWithParam2와 동일한 방법으로 정리하면 아래와 같이 된다.

역연산하기 - 자바스크립트 코드

위의 과정을 모두 거치면, 아래와 같이 된다.
이제 빨간색 네모 친 부분을 계산해야 한다.

memcmp 부분을 보면 PTR_DAT_00404050과 input의 32바이트가 동일해야 함을 알 수 있다. 따라서 memcmp와 가까운 함수부터 위로 올라가면서 역연산을 하면 된다.

  1. a ^ b = c에서 a ^ c = b와 같으므로 이와 같은 속성을 이용한다.
  2. INCWithParam2를 역연산하면 DECWithParam2와 같고, 반대도 같다.

이를 자바스크립트 코드로 구현해보면 아래와 같다.

const DAT_00402068 = [0xde, 0xad, 0xbe, 0xef];
const DAT_0040206d = [0xef, 0xbe, 0xad, 0xde];
const DAT_00402072 = [0x11, 0x33, 0x55, 0x77, 0x99, 0xbb, 0xdd];
const PTR_DAT_00404050 = [0xf8, 0xe0, 0xe6, 0x9e, 0x7f, 0x32, 0x68, 0x31, 0x05, 0xdc, 0xa1, 0xaa, 0xaa, 0x09, 0xb3, 0xd8, 0x41, 0xf0, 0x36, 0x8c, 0xce, 0xc7, 0xac, 0x66, 0x91, 0x4c, 0x32, 0xff, 0x05, 0xe0, 0xd9, 0x91];

const GetStrLen = (input) => {
  return input.length;
};

const XORWithParam2 = (input, array) => {
  const len = GetStrLen(array);
  let result = [...input];
  for (let i = 0; i < 32; i++) {
    result[i] = (array[i % len] ^ result[i]) & 0xff;
  }
  return result;
};

const INCWithParam2 = (input, num) => {
  let result = [...input];
  for (let i = 0; i < 32; i++) {
    result[i] = (result[i] + num) & 0xff;
  }
  return result;
};

const DECWithParam2 = (input, num) => {
  let result = [...input];
  for (let i = 0; i < 32; i++) {
    result[i] = (result[i] - num) & 0xff;
  }
  return result;
};

const decode = (input) => {
  const func_1 = XORWithParam2(input, DAT_00402072);
  const func_2 = DECWithParam2(func_1, -13);
  const func_3 = INCWithParam2(func_2, 77);
  const func_4 = XORWithParam2(func_3, DAT_0040206d);
  const func_5 = INCWithParam2(func_4, 90);
  const func_6 = DECWithParam2(func_5, 31);
  const func_7 = XORWithParam2(func_6, DAT_00402068);

  return func_7;
};

// DH{9ce745c0d5faaf29b7aecd1a4a72bc86}
const flag = String.fromCharCode(...decode(PTR_DAT_00404050));
console.log(`DH{${flag}}`);

& 0xff를 한 이유

C의 char의 크기는 8비트이다. 그리고 자바스크립트는 바이트 크기를 제한하지 않으므로(한계는 있겠지만.. 자세히는 모르겠다) & 0xff를 해서 오버플로우된 비트 말고 하위 8비트만 잘라서 남겨야 한다.

예를 들어 255를 초과하는 값인 260을 아무 처리 없이 출력하면 0x104가 되는데, &0xff를 하면 -> 0xff는 이진수로 1111 1111 -> 비트 & 연산을 하면 -> 1 0000 0100(0x104) & 1111 1111(0xff) -> &연산은 둘 다 1일 때만 1이고 아니면 0이라서 0000 0100이 그대로 나옴. 즉 하위 8비트를 커트하게 됨

let x = 260;  // 8비트 초과 값 (10진수)
console.log(x.toString(16)); // 출력: 104 (0x104)
x = x & 0xFF; // 8비트로 변환
console.log(x.toString(16)); // 출력: 4 (0x04)

정답

9ce745c0d5faaf29b7aecd1a4a72bc86를 넣었을 때 Correct가 출력된 것을 알 수 있다~ 이제 DH{} 안에 넣어서 드림핵에서 인증하면 된다.

후기

자바스크립트로 코딩하며 Ghidra에 있는 데이터를 긁어오는 것이 매우 번거로웠다. 검색을 해봐도 원하는 결과가 나오지 않아서 슬펐는데, 문제 풀이 후기에서 알게되었다.

우클릭 -> Copy special -> python list

이렇게 하면 자료를 배열 형태로 긁어올 수 있다.

그리고 Ghidra에서 Python으로 스크립트를 짤 수 있다고 알고 있는데, 빨리 배워야겠다는 생각을 했다. Ghidra에서 스크립트까지 다 해결할 수 있으면 좋을 것 같다.

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

0개의 댓글

관련 채용 정보