최근에 업무 및 귀차느즘으로 인해 문제를 못풀고
이 문제도 내가 못풀어서 그런지 엄청난 노가다를 통해 풀었다. 아오 짜증 -_-;; 근데 어쩌나 코드분석이 안되는걸...
근데 노가다로 푼다고 뭔가 실력이 느는지? 는 잘모르겠다. 각설하고 시작해보자
오늘 문제는 난이도 2.0 짜리 문제다. Small Horse 작은말? 뭔소린지 모르겠다. 무튼 시작해보자
(https://crackmes.one/crackme/669145bc90c4c2830c82081e)
이문제도 압축을 해제하고 나면 쏘 씸플하다. 패스워드를 입력하고 맞는지 아닌지 푸는 문제다
해당 파일을 ida를 통해 메인을 한번보자
int __cdecl main(int argc, const char **argv, const char **envp)
{
FILE *v3; // rax
__int64 v4; // rax
__int64 v5; // rcx
int v6; // ebx
char *v7; // rdi
char *v8; // rcx
__int128 v10[7]; // [rsp+20h] [rbp-108h] BYREF
__int128 v11; // [rsp+90h] [rbp-98h]
char Buffer[16]; // [rsp+A0h] [rbp-88h] BYREF
__int128 v13; // [rsp+B0h] [rbp-78h]
__int128 v14; // [rsp+C0h] [rbp-68h]
__int128 v15; // [rsp+D0h] [rbp-58h]
__int128 v16; // [rsp+E0h] [rbp-48h]
__int128 v17; // [rsp+F0h] [rbp-38h]
int v18; // [rsp+100h] [rbp-28h]
sub_1400011D0("Password: ");
v18 = 0;
*(_OWORD *)Buffer = 0i64;
v13 = 0i64;
v14 = 0i64;
v15 = 0i64;
v16 = 0i64;
v17 = 0i64;
v3 = _acrt_iob_func(0);
if ( fgets(Buffer, 100, v3) )
{
v4 = -1i64;
v5 = -1i64;
do
++v5;
while ( Buffer[v5] );
if ( v5 && Buffer[v5 - 1] == 10 )
Buffer[v5 - 1] = 0;
do
++v4;
while ( Buffer[v4] );
if ( v4 == 62 )
{
v6 = 0;
v7 = Buffer;
while ( (unsigned __int8)(*v7 - 97) <= 0x19u )
{
memset(v10, 0, sizeof(v10));
v11 = 0i64;
sub_140001230(v10, (unsigned int)v6);
if ( !(unsigned __int8)sub_140001490(v10, 32i64) )
break;
++v6;
++v7;
if ( v6 >= 62 )
{
v8 = "Access granted.\n";
goto LABEL_15;
}
}
}
v8 = "Access denied.\n";
LABEL_15:
sub_1400011D0(v8);
}
system("pause");
return 0;
}
뭐 대충 이런식으로 디컴파일이 된다. 중요한건 모두 메인에있고,
어쨋든 우리는 Access granted. 에 도달하기 위해서는
1. if(v6>=62)
2. sub_140001490
3. sub_140001230
요 3개가 주요한걸 알 수 있다. 코드만 보고 어려우면 그래프를 통해 확인할 수 있다.
(뭐 물론 그 위에서도 체크하는 부분이 있지만 제일 중요한 부분이니까....?)
그러면 위에도 살펴보자. 사실 길지가 않아서 대충 확인해보면 첫 if문 (fgets 부분에서 사용자의 입력을 글자수 만큼 ++ 시키고 결국 해당 값이 62자 인지 체크하고있다.
그리고 v7=Buffer 이기 때문에 입력글자를 1글자씩 가져와서 (소문자 a=97) 97을 빼서 0x19u -> 26 보다 작은지 확인하는거 보면 소문자만 입력으로 받고있구나 하는걸 알 수 있다.
이제 여기부터 내가 엄청 해맨곳인데
sub_140001230 이다. 이건 내가 입력한 첫글자를 가져와서 뭔가 요상한 연산을 수행한다. 이거까지는 뭐 어찌저찌 계산할 수 있다고 생각하자.
그다음 함수인 sub_140001490 이다. 딱봐도 난 뭔가 겁나 복잡해보였다. 그리고 웃긴건 sub_1400012E0을 4번이나 돌려서 뭔짓거리를 한다는거다. 그래서 처음에 하루이틀은 이거 분석했다. 그래봤자 하루에 2시간 정도지만 이거 하다가 대가리가 터지고...... 나 재능없나봐 흑흑흑... 생각했다.
그래서 결국에 이거는 진짜 뭔가 내영역이 아닌거같아서 코드분석은 포기하고 디버깅을 해버리기로헀다..
갓버깅..... 디버깅없으면 어찌문제풀지 나도 잘 모르겠다 ㅎ_ㅎ;
그러면 전체 입력값이 62자나되고 , a-z까지 입력값인데 어떻게할껀가? 브루트 포싱으로 26^26은 개오바쟈나...?
그리고 예전처럼 마지막에 패스워드 값을 비교하는것도 아니고 내 입력값을 하나하나 무슨 이상한 연산을 수행하고 해당 값이 0.5 보다 큰지 아닌지 비교해서 결과를 낸다. 뻐킹...
그래서 나는 그냥 강제로 코드를 아래처럼 패치했다. 우선 현재의 흐름은 아래그림과 같다.
요 함수를 아래와 같이 패치를 했다.
그리고 중요한 부분인데 sub_140001230에서 sub_1400012E0의 결과값을 받아와서 break 할지말지 결정하는 부분인데.
setnbe al 부분이 al 레지스터 값(0,1)에 따라서 이제 break 될지말지 결정된다 볼 수 있음. 따라서 여기에 bp를 걸고 각 문자를 돌면서 al 레지스터 값을 보면 정답을 맞출 수있다.
나의 전체 시나리오는 아래와 같다.
1. 코드패치를한다.
2. a로 가득한(62글자) 입력을 보낸다.
3. 각 a자리수에 따라 bp걸렸을 때 al 레지스터 값을 확인해 0,1 인지 확인한다.
3. b로 가득한(62글자) 입력을 보낸다..
.
.
.
n. z로 가득한(62글자) 입력을 보낸다.
n+1. 각 z자리수에 따라 bp걸렸을 때 al 레지스터 값을 확인해 0,1 인지 확인한다.
물론 개노다가임 적어도 26번 하고 마지막 문자열을 조합해야 하니까...?
그런데 이거 손으로 하는거 솔직히 개오바쟈나....
그래서 Idapython을 사용한다.
아래 코드는 bp 걸렸을때 al 값을 뱉어는 코드다
무튼 이걸이용해서 풀면 끄-읏
'Technical Docs > Reversing' 카테고리의 다른 글
Crackmes.one - 4 (7) | 2024.07.24 |
---|---|
Crackmes.one - 3 (4) | 2024.07.22 |
Crackemes.one - 2 (0) | 2024.07.19 |
Crackmes.one - 1 (0) | 2024.07.17 |