-
[reversing.kr] PEPasswordWargame/study 2022. 1. 4. 02:02
1. Description
파일을 다운로드하면 다음과 같이 두 개의 .exe 파일을 확인할 수 있다.
첨부 파일 각 파일을 실행해 보자.
1) Original.exe
Original.exe 실행 위와 같이 "Password is ????????????" 문자열을 포함하는 메시지 박스가 출력되고 종료된다.
2) Packed.exe
Packed.exe 실행 대화 상자가 나타나고 문자를 입력할 수 있다. 따로 확인 버튼은 없다.
2. Analysis
1) Original.exe
먼저 Original.exe 파일을 분석해보겠다. Exeinfo PE로 패킹 유무를 검사해보았다.
Exeinfo PE - Original.exe 따로 패킹은 되어 있지 않다. 이어서 ida로 파일을 열어보았다.
IDA - Original.exe 스택에 선언된 변수들에 대하여 do-while 반복문으로 xor 연산을 진행하고 메시지 박스를 호출한다. 메시지 박스 호출 부분에 bp를 설정하고 프로그램을 실행하면 아래와 같이 문자 '?'에 해당되는 아스키코드 값이 Text 배열에서 연속적으로 나타나는 것을 확인할 수 있다.
password 부분 더 이상 단서를 얻을 수 있지 않을 것 같아서 Packed.exe 파일에 대해 분석을 진행해보았다.
2) Packed.exe
Exeinfo PE로 Packed.exe를 열어보면 아래와 같이 패킹된 파일임을 알 수 있다.
Exeinfo PE - Packed.exe 아무래도 실행 화면에서 "Password Check" 라는 캡션을 갖는 대화 상자가 나타나므로 적절한 비밀번호를 입력했을 때 플래그를 얻는 구조인 것 같았다. x32dbg로 파일을 열어 실행시킨 후, 입력을 받았을 때 호출되는 windows API가 무엇일지 고민하면서 bp를 이것저것 설정하다가 user32.dll에 있는 SendMessageA API에 대응되는 것을 확인했다.
SendMessageA API bp 적중 콜 스택을 확인해보니 아래와 같이 packed.exe의 영역에서 SendMessageA API 관련한 호출이 일어난다.
콜스택 해당 지점으로 이동해서 호출되기 직전 부분에 bp를 설정하고 분석을 진행해보았다.
bp 설정 0x409090 주소의 call dword ptr ds:[edi+402923] 명령어에 의해 SendMessageA API가 호출되고, 그 결과 값(사용자가 입력한 문자열)이 다음 명령어 pop esi에 의해 esi 레지스터에 담기게 된다. 이후에 call <packed.sub_4091D8> 명령어가 호출되는데 해당 부분은 아래와 같다.
password checking...? 수상한 xor 연산 / ror 연산이 존재한다. 입력한 문자열에 대해 지지고 볶고 연산을 진행한다. 이후 ret에 의해 돌아가는 곳은 아래와 같다.
결과값 비교(검증) cmp 명령어를 통해 앞의 password checking 부분의 결과값이 0xE98F842A와 같은지 비교한 후, 결과에 따라 분기하게 된다. 여기서 같지 않을 경우 아무 일도 일어나지 않는다. 처음에는 이 비교 연산을 통과하기 위해 올바른 입력 비밀번호 값을 찾아야 하나 싶었다. 하지만 역연산이 불가능한 구조고, 따로 문자열 길이에 대한 제한이 주어지지 않을 상황이므로 브루투포싱도 시간이 너무 오래 걸릴 것으로 생각했다. 어쩔 수 없이 004091A6 주소의 분기문에서 zero flag 값을 바꿔 검증을 우회하고 계속해서 분석을 진행해나갔다.
분석을 진행하다 보면 아래와 같은 코드를 확인할 수 있다.
decoding routine...? pushad와 popad. 패킹 된 파일에 자주 등장하는 어셈블리 명령어다. pushad를 통해 레지스터 값을 저장해주고 디코딩 루틴을 수행해 code 영역을 복원한 후, popad를 통해 레지스터를 복원하고 OEP로 점프하는 구조로 예상된다. 이 시점에 마찬가지로 esi 레지스터에는 입력한 문자열의 주소가 담겨 있으며, edi 레지스터에는 프로그램의 entry point 주소가 담겨 있다.
3. Solving
본격적인 디코딩은 0x40921F 주소에서 시작되며, 그 전에 0x4091DA 주소 영역을 두 번 호출(password check 루틴)한 결과를 eax 레지스터와 ebx 레지스터에 저장한다.
decoding routine...? 디코딩 루틴의 경우 다행히도 크게 복잡해 보이지는 않는다. 4 byte 단위로 code 영역에 대해 xor연산을 진행하며, 1000번 반복하게 된다. 메모리 영역을 봐도 해당 부분이 디코딩 루틴일 것이라 생각할 수 있다.
packed.exe 메모리 맵 문제는 code 영역에 대해 xor 연산을 적용시킬 값이다. 앞에서 zero flag를 통해 검증을 우회했으므로 적합한 비밀번호를 찾지 못한 상황이다. 따라서 eax, ebx값을 계산해낼 수 있어야 하는데, 이때 Packed.exe 파일의 xor 연산을 수행해서 디코딩이 되었을 때, Original.exe 파일과 값이 같을 것이라 생각했다. Original.exe의 코드 영역의 일부는 아래와 같다.
Original.exe 코드 영역 Packed.exe의 동일 영역의 일부는 아래와 같다.
Packed.exe 코드 영역 위의 파란 선으로 표시한 부분만 비교했을 때, Packed.exe에 xor을 적용했을 때 Original.exe가 되면 된다. 이 값은 간단하게 두 부분을 xor 해서 계산해낼 수 있으며, 이는 eax 레지스터의 값이 된다.
packed(0x014cec81) ^ original(0xb6e62e17) = eax(0xb7aac296)
eax 레지스터의 값은 간단하게 구할 수 있지만, ebx 레지스터는 역연산으로 계산할 수 없었다. ebx 레지스터가 32 bit 정수 범위만 표현 가능하므로 그냥 0부터 브루투 포싱을 진행하는 코드를 아래와 같이 작성했다.
#include <cstdio> unsigned int rol(unsigned int x, int amount) { unsigned int res1 = (x << amount) & (unsigned int)((unsigned long long)(1 << 32) - 1); unsigned int res2 = x >> (32 - amount); return res1 | res2; } unsigned int ror(unsigned int x, int amount) { return rol(x, 32 - amount); } int main() { unsigned int ans; for (ans = 0; ans < 0xffffffff; ans++) { unsigned int ebx = ans; unsigned int eax = 0xb7aac296; unsigned int al = eax & 0xff; ebx = rol(ebx, al % 32); eax = eax ^ ebx; unsigned int bh = (ebx & 0xffff) >> 8; eax = ror(eax, bh % 32); if (eax == 0x5a5a7e05) printf("ebx : 0x%08x\n", ans); } return 0; }
실행하면 다음과 같이 두 개의 값을 확인할 수 있다.
ebx 레지스터 첫 번째 값은 두 번째 반복 디코딩에 대해서만 만족하고 세 번째 반복에서는 올바르게 복원시키지 못했다. 두 번째 값의 경우 올바르게 복원시키는 것을 확인했다. 구한 값을 디코딩이 진행되기 직전에 eax, ebx 레지스터에 세팅을 하고 프로그램을 실행시키게 되면 아래와 같이 패스워드 값이 출력되는 것을 확인할 수 있다.
flag 4. Comment
모든 리버싱 문제가 그렇겠지만, 풀고나면 별거 없는데 그 과정에서 삽질을 너무 많이 하게 되는거 같다. 본래 다시 리버싱 공부를 하면서 PE 구조에 대해 다시 되짚고 관련된 문제를 풀어보기 위해 PEPassword라는 이름에 이끌려 문제를 풀기 시작했지만 PE 구조랑은 전혀 관계없었다. 이번 문제를 푸는 데 있어서 시간을 제일 많이 잡아먹은 부분은 사용자의 입력을 받는 관련 Windows API를 찾는 부분이었다.
다른 분들의 풀이를 보았는데 ebx 레지스터를 계산하는 과정에서 인라인 어셈블리를 활용해서 간단하게 작성한 분도 계셨다. 리버싱 관련 flag 계산 코드를 작성할 때 파이썬으로 작성할 때 살짝 불편한 부분이 있었는데 이렇게 인라인 어셈블리를 활용해서 코드를 효율적으로 작성하는 부분도 고려해보면 좋을 것 같다.
https://p3ngdump.tistory.com/21
reversing kr PE Password
문제를 받아 zip파일을 풀면 이렇게 두 프로그램이 나온다. Original.exe 를 실행시키면 위 그림과 같이 메세지박스하나가 뜬다. 그리고 Packed.exe를 실행시키면 Password를 체크한다. 근데 이 박스안에
p3ngdump.tistory.com
#include <cstdio> int main() { unsigned int temp; unsigned int firsteax = 0xb7aac296; unsigned int nexteax = 0x5a5a7e05; for (unsigned int i = 0; i < 0xffffffff; i++) { __asm { mov eax, firsteax mov cl, al mov ebx, i rol ebx, cl xor eax, ebx mov cl, bh ror eax, cl add ebx, eax mov temp, eax } if (temp == nexteax) printf("ebx : 0x%08x\n", i); } return 0; }
반응형'Wargame > study' 카테고리의 다른 글
[dreamhack.io] rev-login (0) 2022.03.02 [HackCTF] BabyMIPS (0) 2022.02.22 [SCTF 2021] secure_enough (0) 2022.01.26 [dreamhack.io] Textbook-CBC (0) 2022.01.13 [SCTF 2021] memory (0) 2021.12.30