안녕하세요 파일입니다~ 오늘은 이전에도 계속 하고 있었던 Abex CrackMe의 3번 풀이입니다.
앞 2번 풀이에서 했던 것 처럼 IDA는 분석용으로 쓰고 동적 디버깅은 올리디버거로 해보겠습니다!
또 앞부분에서 프로그램의 설명법은 제법 자세히 설명을 드려서 앞에서 이미 설명드린건 넘어가고 새로 알아둬야할 내용만 작성하도록 하겠습니다.
다운로드는 여기서~
실행
처음 실행 시 확인을 눌러서 키 파일을 확인해보라고 나옵니다
프로그램 생성시 키 파일이 자동 생성되는것도 아니고 필요한 키 파일이 뭔지도 안알려주기 때문에 당연히 없다고 나옵니다. 하지만 우리는 리버서니깐 바이너리 파일을 디스어셈블러로 분석해서 이 프로그램의 동작 원리를 알아낼 수 있겠죠 ㅎㅎ 시작 해봅시다. 이 녀석이 어떤 기계어를 실행하고 있을지~
분석
이번 CrackMe #3번은 IDA를 사용하면 올리디버거에 비해 많은 도움을 받을 수 있습니다. 보시다 싶이 화면에 그래프로 보이는 요 코드가 이 프로그램의 끝입니다. 그리고 프로그램 시작점 (Entry Point) 이 맨 첫줄 (주소 00401000) 부터 바로 보이는것으로 보아 이번 프로그램도 CrackMe #1번 처럼 어셈블리어로 작성이 되지 않았나 싶군요.
확인해보니깐 델파이로 만들었다고 나오는군요 -.-
이상해서 Crackme 1번도 확인해보니깐 델파이라고 하고.. 분명 Crackme 1번은 어셈블리어로 만들었다고 줏어들었는데 Crackme 3번은 델파이인걸까요? 뭐 어쨌던 간에 우리는 바이너리 파일만 디스어셈블리해서 어셈블리어로 분석할꺼니깐요. 그렇게 중요한 부분은 아닙니다.
일단은 위에 잡다한 내용들은 IDA가 프로그램을 분석하고 자동 생성해준 주석이니깐 무시하구요. 저기 start proc near을 넘어서 push 0부터 시작하는게 프로그램의 시작 코드입니다.
아까도 말씀드렸듯이 00401000 위치에서 PUSH 0을 수행함으로써 시작합니다.
일단 분기가 일어나기 전에 초반에 CreateFileA 라는 함수를 호출하고 있습니다. 윈도우 API에서 파일을 생성하는 함수입니다. 그리고 맨 마지막 문자열을 보니깐 대충 abex.l2c 라는 파일을 생성하고 있는 걸로 보이구요.
?? 사실 처음에 의아했습니다. 왜 파일 생성 함수가 있는데 abex.l2c 파일은 만들어지지 않은거지??
일단은 IDA에서 디컴파일 플러그인이 있다면 어셈블리어 코드를 C언어로 비슷하게 복구할 수 있습니다. 완벽히 C언어로 복구해주는건 아니고 C언어로 보기 좋게 바꿔서 대충 감을 잡을 수 있게 해주는데요. 완벽한 C코드는 아니고 가짜 C코드라고 해서 Pseudo - C 라는 모습으로 보여줍니다.
간단히 코드 화면에서 F5(단축키)를 누르면 어셈블리어를 C언어로 비슷하게 만들어서 보여줍니다.
사실 이 기능을 사용하면 이 문제는 정말 날로 먹을 수 있습니다 -,- 분석할 필요도 거의 없이 답을 알아낼 수 있으니깐요.
처음 공부할땐 쓰면 안좋은 기능이긴한데 일단은 3번까지 진행해서 나름 리버싱에 대한 감을 많이 잡았으니깐요 있는 기능이니깐 써봅시다 ㅎㅎ.
F5 를 꾸욱 눌러줍니다.
401088 : call analysis failed
?? 디컴파일이 실패했다고 나오네요.
401088 주소 쪽에서 함수를 호출하는데 여기서 분석이 실패한거 같네요.
맨 아래쪽에 보니 start endp 부분에서 분석이 실패했다고 빨간색으로 표시되네요.
매번 IDA 쓸때마다 crackme 풀면 저기서 분석 실패가 뜨던데 -.- 이유는 잘 모르겠군요.
저 부분을 잘 해석하지 못하나 봅니다.
확인해보니 저 부분은 함수 호출할때 함수 원형에 적힌 인자값하고 다르게 호출을 해서 그렇다는데요
start endp 저 라인을 클릭하고 Y를 누르니깐 수동으로 type declaration 을 입력할 수 있었습니다.
그런데.. call ExitProcess 를 호출할때부턴 프로그램이 사실상 종료되는거라 저 부분은 필요가 없거든요.
종료하는것도 귀찮고 하니 그냥 저 부분을 통째로 날려버리도록 하겠습니다. 일단은 디컴파일 기능을 수행하려면 저렇게 해석 불가한 부분을 고쳐줘야 하니깐요..
지울 그래프 부분을 오른쪽 클릭 - Undefine 을 하면 저 부분 코드를 통째로 날려버릴 수 있습니다.
네 보시다 싶이 필요 없는 부분이 날라갔구요 ㅎㅎ 이제 F5를 다시 눌러줍니다.
이제 Pesudocode-A 탭이 열리며 C언어 형태로 비슷하게 복구를 해주는데요. 코드를 대충 읽어보니깐 CreateFileA로 파일을 생성하고 , 그 생성한 파일이 18바이트면 성공 메세지를, 아니면 실패 메세지를 출력하는 코드였군요.
아까 생성하려는 파일 이름은 FileName으로 나와있는데 아까도 확인했지만 abex.l2c 라는 이름입니다.
* 기억이 안나면 저 FileName을 더블클릭하면 저 변수가 어떤 값을 지니고 있는지 IDA가 바로 보여줍니다.
어쨌던... 보시다 싶이 IDA의 F5 디컴파일 기능이 매우 사기적이기 때문에 이렇게 단박에 해석이 가능한 경우가 있습니다. 특히 이렇게 간단한 프로그램은 더더욱요.!
일단은 프로그램 풀이는 사실상 끝났는데 그냥 abex.l2c 라는 파일을 수동으로 생성해준다음에 영어로 18글자(==18 바이트)를 적어주면 끝나겠습니다.
파일을 수동으로 생성하고 프로그램 같은 경로에 a를 18글자 적어줍니다.
저는 Notepad++로 작성해주도록 하겠습니다.
* 영어는 1글자에 1바이트, 한글은 1글자에 2바이트 입니다.
3번을 실행하면 네 성공했다고 나오네요 ㅋㅋ.
근데 아직도 의문이 풀리지 않은건 왜 저 CreateFileA 함수는 파일을 생성하지 않았는가? 입니다.
풀이를 여기서 끝내긴 아쉬우니깐 좀더 봅시다.
다시 본래 코드로 돌아왔는데요. 저 CreateFileA 함수의 원형을 찾아보니깐 아래와 같습니다.
HANDLE CreateFileA(
[in] LPCSTR lpFileName,
[in] DWORD dwDesiredAccess,
[in] DWORD dwShareMode,
[in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,
[in] DWORD dwCreationDisposition,
[in] DWORD dwFlagsAndAttributes,
[in, optional] HANDLE hTemplateFile
);
//출처 : https://learn.microsoft.com/ko-kr/windows/win32/api/fileapi/nf-fileapi-createfilea
함수 설명 : 파일 또는 I/O 디바이스를 만들거나 엽니다.
반환값 HANDLE : 파일 또는 디바이스에 액세스하는 데 사용함.
파일을 생성해주는 기능을 지원함과 동시에 HANDLE 이라는 값을 반환해서 이걸로 생성한 파일에 접근할 수 있게 해주나 보군요. HANDLE이 정확히 무슨 타입 값인지 궁금했는데 그냥 void 포인터입니다.
또 CreateFileA 래서 그냥 파일 생성 기능만 있는줄 알았더니 인자값에 따라 파일을 열수도 만들수도 있는 거 같습니다.
제가 윈도우 API를 공부하고 글을 썼으면 더 좋았을탠데 .. 아쉽네요 ㅎㅎ; 리눅스 시스템콜 open() 이랑 비슷한 함수군요.
#include <stdio.h>
#include <windows.h>
int main()
{
HANDLE hFile = CreateFileA("abex.l2c", 0x80000000, 0, 0, 3u, 0x80u, 0);
if (hFile == (HANDLE)-1)
{
printf("CreateFileA failed");
}
return 0;
}
>> CreateFileA failed
아까 어셈블리로 CreateFileA 윈도우 API를 호출하는 부분을 C언어로 그대로 옮겨왔구요.
abex.l2c가 없는 상태에서 실행하면 CreateFileA가 실패해서 오류 값이 반환됐다고 나옵니다.
보통 파일 생성이나 읽는 함수 호출해서 프로그래밍 해보신 분들은 알겠지만 저렇게 파일 생성하는 함수가 실패한 경우는 인자값에 준 옵션 값들이 틀려서 그렇습니다.
예를 들어서 파일 열때 새로운 파일이 없으면 새로 생성하고 만드는 옵션이 숫자 1, 새로 생성하진 않고 그냥 읽는 옵션이 숫자 2라고 가정해보면 파일이 없는 상태서 옵션을 2로 주고 실행하면 새로 생성하진 않고 읽는 모드기 때문에 파일이 없으면 오류 메세지를 뱉고 아무것도 하지 않겠죠 -.-
위의 예제도 CreateFileA에 인자값이 잘못되어서 abex.l2c 파일이 제대로 생성되지 않는겁니다.
(물론 정확히는 잘못되었다기 보다는 인자값을 줄 때 그냥 파일을 생성하게 지시한게 아니라 읽는 목적으로만 인자값을 줘서 그렇습니다.)
HANDLE hFile = CreateFileA("abex.l2c", 0x80000000, 0, 0, 3u, 0x80u, 0);
예를 들어서 마이크로소프트에서 공식 제공하고 있는 CreateFileA의 dwDesiredAccess 값은 1 , 2, 4 , 0 값 중에 하나여야 합니다. 그런데 이 어셈블리 코드로 호출하는 CreateFileA의 경우 dwDesiredAccess에 0x8000.. 이라는 이상한 16진수 값을 제공했네요. (함수 원형을 보시면 알겠지만 2번째 인자값 이름이 dwDesiredAccess 입니다.)
또 나머지 인자값을 다 확인해봐도 파일 생성이 안되도록 값을 전부 꼬아놨더라구요 ㅡㅡ;
뭐 이것도 파일 생성하도록 패치해보라고 킹받음(?) 을 위해 남겨둔게 아닌가 싶습니다.
crackme 3번 프로그램을 실행하면 적어도 abex.l2c가 없으면 자동으로 생성할 수 있도록 추가로 패치(수정)해보도록 하겠습니다.
#include <stdio.h>
#include <windows.h>
int main()
{
HANDLE hFile = CreateFileA("abex.l2c", 2, 1, 0, 4, 0x80u, 0);
if (hFile == (HANDLE)-1)
{
printf("CreateFileA failed");
}
return 0;
}
일단은 abex.l2c 파일을 단순히 여는게 아니라 없으면 생성까지 시키는 경우 인자값을 저렇게 주면 됩니다.
이 인자값대로 어셈블리 코드에서 인자값을 전부 수정해주면 되겠군요.
이제 어셈블리어로 이동해서 패치해봅니다.
인자값에 맞춰서 전부 어셈블(인라인 어셈블리 코드 수정) 해줬습니다.
사실 IDA에서도 디버깅 맞추고 할 수 있는데 동적으로 디버깅하는건 올리디버거가 익숙해서 그런지 훨씬 편하더라구용...
나중에 시간이 나면 다음 크랙미 4번이나 5번은 IDA로 디버깅 + 패치 하는 모습도 보여드리도록 하겠습니다.
이렇게 패치해주면 편안하게 파일도 생성해줍니다. 이제 다시 실행하기전에 여기에 18글자만 적어주면 끝나겠죠~
사실 풀이는 IDA에서 F5 누르고 나서부터 끝난거지만 뭐 공부차 조작해봤다고 생각합니다 ㅎㅎ
그리고 이번 크랙미 3번은 프로그램 구조 자체가 워낙 단순해서 사실 올리디버거로 위에서 부터 아래로 읽어도 이해하시는덴 충분할 겁니다. 사실 2번이 더 어려웠는데.. 이걸 2번으로 둬야 하지 않았을까..? 싶네요
'보안 강좌 > CrackMe' 카테고리의 다른 글
[Solution] Abex' CrackMe #5 풀이 [완결] (0) | 2023.01.01 |
---|---|
[Solution] Abex' CrackMe #4 풀이 (0) | 2023.01.01 |
[Solution] Abex' CrackMe #2 풀이 (IDA, OllyDbg 사용) (0) | 2022.12.31 |
[Solution] Abex' CrackMe #1 풀이 (0) | 2022.12.27 |