문제 설명
문제 풀이
- 사용했던 툴 : x64dbg
- 문제 접근 : 문자열 참조 검색을 통해 main 함수를 찾고, correct나 wrong을 출력하게 만드는 비교 연산 함수 찾기
일단 다운받은 문제 파일을 x64dbg에서 열면 다음과 같은 창이 나온다.
바로 문자열 참조 검색 기능을 통해 Main 메소드를 찾아준다.
첫번째로 뜬 "Input : " 문자열을 클릭하면 main 메소드를 찾을 수 있다.
조금만 밑으로 내려가면 "Correct"나 "Wrong"을 출력하는 부분을 볼 수 있는데, 140001000에서 flag 문자열과 사용자가 입력한 문자열을 비교한다는 것을 어렵지 않게 알 수 있다.
노란색으로 하이라이트 처리된 것들 중에 140001000을 클릭해보자.
아마 첫번째 줄부터 ret 명령을 수행하는 14000105C 부분까지가 비교 연산을 수행하는 함수인 것 같다.
문자열과의 비교연산은 결국 반복문을 돌며 한글자씩 이루어지기 때문에 우리가 눈여겨 볼 부분은 바로 반복을 수행하는 부분이다. 따라서 나는 이 문제를 풀 때 초기값 세팅을 하는 부분과 반복문을 돌며 연산을 수행하는 부분을 나눠서 보려고 했다.
(잘 알지도 못하는 어셈블리어를 여러줄 보다보면 머리 아프다.)
연산을 수행하는 부분은 결국 140001024부분부터이다. 그 윗부분까지는 반복문을 돌기 위해 미리 스택 포인터에서(rsp) 0x18(24)의 값을 빼두고 스택의 현재 위치에 0을 저장한다는 내용이다. 또한, 14000101A부터 140001022까지는 반복문의 조건을 설정한다는 부분으로 보면 된다. for(i = 0; i <= 0x18; i++)와 같이 말이다.
140001024부분부터 return을 수행하기 위해 스택을 복구하기 전의 상태인 14000104F까지(for문의 안) 라인별로 분석한 결과는 다음과 같다.
140001024 : rsp에서 값을 가져와 rax에 저장
140001028 : rcx를 0x140003000로 설정(배열 시작 주소)
14000102F : rcx+rax의 주소에 있는 바이트 값을 eax로 가져오기
140001033 : rsp의 값을 rcx로 가져오기
140001037 : rsp+20를 rdx에 저장
14000103C : rdx+rcx 주소의 바이트 값을 ecx로 가져오기
140001040 : ecx 값을 rsp에 있는 값과 XOR 연산을 수행
140001043 : edx에 rsp 값을 저장
140001046 : ecx = ecx + (edx * 2)로 설정
140001049 : eax와 ecx 값을 비교
14000104B : 같으면 0x140001051로 이동(루프 돌기)
14000104D : eax = 0으로 설정
14000104F : 루프 빠져나오기
일단 배열의 시작 주소인 140003000이 덤프창에서 어떤 아스키 코드 값을 출력하는지 보았다.
뭔가 느낌 온다. 잘 조합하면 무슨 문자열이 나올 것만 같다...?
어셈블리어를 분석해서 적어두었지만, 사실 정확하 이해하기가 어려워서 분석해둔 부분을 복사해 ChatGPT에게 C언어로 바꾸어 달라고 했다...ㅎㅎ
#include <stdio.h>
#include <stdint.h>
int chall3(const char* input) {
const uint8_t* data = (uint8_t*)0x140003000; // 데이터 테이블
int i = 0;
while (i < 24) {
uint8_t val1 = data[i];
uint8_t val2 = input[i] ^ i;
val2 = val2 + (i * 2);
if (val1 != val2) {
return 0; // 실패
}
i++;
}
return 1; // 성공
}
여기서 data가 아까 그 이상했던 문자열
을 말하는데 이 문자열이 (input[i] ^ i) + (i * 2)의 연산을 통해 나온것이기 때문에 우리가 input[i]값을 구하기 위해서는 xor의 역연산을 수행해야만 한다.
XOR 연산은 다음과 같은 성질을 가진다.
A ^ B = C에서 임의의 값 B는 A ^ C = B를 만족한다.
따라서 위의 이상한 문자열들을 dump[i]라고 두면 dump[i] = (input[i] ^ i) + (i * 2)라고 볼 수 있고, 이의 역연산은
input[i] = (dump[i] - (i * 2)) ^ i라고 볼 수 있다.
역연산을 수행하는 C언어 코드를 간단히 정리한 결과는 다음과 같다.
#include <stdio.h>
int main() {
char dump[] = {0x49, 0x60, 0x67, 0x74, 0x63, 0x67, 0x42, 0x66, 0x80, 0x78, 0x69, 0x69, 0x7B, 0x99, 0x6D, 0x88, 0x68, 0x94, 0x9F, 0x8D, 0x4D, 0xA5, 0x9D, 0x45};
for (int i = 0; i < 24; ++i) {
printf("%c\n", ((dump[i] - 2 * i) ^ i));
}
return 0;
}
그리고 실행해보면 다음과 같은 문자열이 나온다.
이를 플래그 형식에 맞추어 제출하면
'Security > Reverse Engineering' 카테고리의 다른 글
rev-basic-6 풀이 (0) | 2025.02.23 |
---|---|
rev-basic-3 풀이(IDA) (0) | 2025.02.20 |
리버싱 기초 문제 풀이 (0) | 2025.02.04 |
abex-crackme#2 풀이 (0) | 2025.02.04 |
rev-basic-2 풀이 (0) | 2025.01.22 |