본문 바로가기

dreaMhack/Reverse Engineering

[드림핵] rev_basic_6 풀이/어셈블리어 해석

https://dreamhack.io/

 

https://dreamhack.io/wargame/challenges/20/

문제 파일을 다운로드받고 압축을 풀어준다

 

 

 

1. x64bug 해석

ctrl+f9를 누르며 진행하다 화면에 wrong이 출력됐을 때 함수 코드

본 함수의 코드들인데 여기에서 주목해야 할 점은 correct와 wrong을 출력해주는 부분이다

이 이전에 있는 함수에서 해당 문자열을 고를 반환값을 줬을 가능성이 높기 때문이다

call challo.7FF799781000

따라서 위의 코드 속 함수 주소를 더블 클릭 해주면 우리가 해석해야하는 함수의 코드들이 나오게 된다

 

이 함수에서 쓰이는 어셈블리어 함수 종류

mov 두 번째에서 첫 번째로 데이터 이동 jmp 무조건 분기
movzx 길이가 일치하지 않아도 이동(빈 공간 0) jae 결과가 크거나 같으면 분기(부호화 안 된 수)
movsxd 길이가 일치하지 않아도 이동(빈 공간 1) je 결과 값 0이면 분기(이동)
sub 캐리 포함하지 않은 뺄셈 lea 오프셋(데이터 영역) 값을 레지스터로 로드
add 캐리 포함하지 않은 덧셈 xor 배타논리합(값이 달라야 1)
inc 내용 1 증가 ret 스택에 push된 주소로 복귀(리턴)
cmp 두 인자 내용 비교(같으면 0)    

 

mov: 이전에 사용했던 rcx 속에 있는 수를 rsp+8에 저장해두고

sub: rsp에 18을 빼 메모리를 확장해준다

(= 이 함수 내에서 사용할 스택을 다른 위치로 지정해주는 코드)

 

rsp, jmp: 반복 횟수를 0으로 초기화해주고 movsxd 명령어가 있는 위치로 무조건 분기한다

movsxd: 반복 횟수를 rax에 넣은 후

cmd, jae: 저장된 반복 횟수가 누적 12를 넘기거나 같아지기 전까지는 jae 명령어를 무시한다

만일 수가 같아지면(for 문이 끝나면) jae는 포인터를 함수가 끝나는 곳으로 이동시킨다

 

movsxd, mov: 반복 횟수를 rax에 넣고, 입력값을 rcx에 넣는다

movzx: rcx+rax 즉, 입력값의 반복횟수 번째의 문자를 eax에 넣는다

lea, movzx: 해당 주소에 있는 문자열을 rcx에 넣고, 문자열의 (입력값의 반복 횟수 번째 문자) 번째 문자를 eax에 넣는다

 

movsxd, lea: 반복횟수를 rcx에 넣고, 해당 주소에 있는 문자열을 rdx에 넣는다

movzx, cmp: (문자열 주소 + 반복 횟수) 번째 문자를 ecx에 넣고 전 사진에서 얻은 eax와 비교한다

je: 비교한 값이 같으면 함수의 초반으로 다시 돌아가는 jmp chall......1012 명령어로 이동한다

xor, add, ret: 같지 않으면 같은 수를 xor 해 반환 값을 0으로 만들고, 스택을 함수에 들어오기 이전으로 돌린 뒤 함수에서 빠져나오게 된다.

 

 

 

 

 

2. Ida 해석

프로그램을 시작하고 f5(디컴파일)를 누르면 나오는 화면

ida는 익숙한 C언어 형태로 디컴파일해줘서 x64dbg보다 훨씬 쉽게 해석할 수 있다

위의 코드를 봐도 correct를 프린트해주는 곳의 if문만 잘 보면 해결할 수 있을 것 같다는 생각이 든다

 

if 문 안에 있는 함수(sub_140001000)를 더블클릭하면 내부로 들어와진다

함수의 디컴파일 버전을 보니까 x64dbg보다 훨씬 이해하기 쉬운 형태를 보여주고 있다

byte_140003000에 있는 정답 문자열이 입력값과

연산 결과값과 같아야 correct를 프린트한다는 걸 알 수 있다

 

정답 문자열 = 특정 문자열[입력값]

입력값 = 장답 문자열이 갖는 특정 문자열 안에서의 인덱스 값

 

정답 문자열이 있는 주소를 더블 클릭하면

Hex View-1창에 해당 위치에 있는 문자열들이 나열돼 있다

(위에 있는 두 줄이 정답 문자열, 두 줄 제외 모든 문자가 특정 문자열들)

우리가 유추한 식을 이용해 c언어를 코딩해 정답을 알아보자

 

#include <stdio.h>

int main() {
	unsigned char chars[] = { 0x00, 0x4D, 0x51, 0x50, 0xEF, 0xFB, 0xC3, 0xCF, 0x92, 0x45, 0x4D, 0xCF, 0xF5, 0x04, 0x40, 0x50, 0x43, 0x63 };
	unsigned char res[] = { 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B,
		0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2,
		0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5,
		0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07,
		0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0,
		0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1,
		0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D,
		0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92,
		0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC,
		0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, 0x60, 0x81, 0x4F,
		0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, 0xE0, 0x32,
		0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7,
		0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
		0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B,
		0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1,
		0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE,
		0x55, 0x28, 0xDF, 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 };
	unsigned char num = "";

	for (int i = 0; i < 18; i++) {
		for (int k = 0; k < 240; k++) {
			if (chars[i] == res[k]) {
				num = k;
				break;
			}
		}
		printf("%c", num);
	}
	return 0;
}

 

 

 

 

 

정답: DH{Replac3_the_w0rld}