실행해보면, L O T T O 를 출력하고, 숫자를 입력하라한다.
그리고는 화면이 cls 된다.
편하게 디컴파일해 정적분석을 해보자.
1
2
3
4
5
|
do {
wprintf(L"\n\t\tL O T T O\t\t\n\n"); wprintf(L"Input the number: ");
wscanf_s(L"%d %d %d %d %d %d", &v13, &v14, &v15, &v16, &v17, &v18);
wsystem(L"cls");
Sleep(0x1F4u);
|
cs |
숫자 6개를 입력받고, cls 한 뒤, 0.5초 쉰다.
1
2
3
|
do
*(&v18 + ++v1) = rand() % 100;
while ( v1 < 6 );
|
cs |
v19~v25 까지 srand(time(0)) 해서 나온 100이하 rand값 6개를 저장한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
v2 = 1
v3 = 0; v4 = 0i64;
byte_1400035F0 = 1;
while ( *(&v19 + v4) == *(&v13 + v4) )
{
v4 += 4i64;
++v3;
if ( v4 >= 24 )
goto LABEL_9;
}
v2 = 0;
byte_1400035F0 = 0;
LABEL_9:
;
|
cs |
v3 은 0이고, v13~v18(입력한숫자), v19~v25(rand값) 틀릴때까지 비교한다.
6개 다 맞으면 LABEL_9으로 가서 v2가 1인 상태가 유지되고, v3=6 이 되었으니 반복문이 종료된다.
못맞추면 다시입력받는다.
1
2
3
4
5
6
7
8
9
10
|
v26 = 0x5C;
v25 = 0xB8;
v27 = 0x8B;
v30 = 0xB8;
v28 = 0x6B;
v6 = 0i64;
v29 = 0x42;
~~~~~~~~~~~~~~~
v49 = 0xF2;
v50 = 0;
|
cs |
변수를 초기화하고,
1
2
3
4
5
6
7
8
9
10
11
|
do
{
v7 = byte_140003021[v6 - 1];
v6 += 5i64;
*(&v22 + v6 + 1) ^= (v7 - 12);
*(&v23 + v6) ^= (byte_140003021[v6 - 5] - 12);
*(&v23 + v6 + 1) ^= (byte_140003021[v6 - 4] - 12);
*(&v24 + v6) ^= (byte_140003021[v6 - 3] - 12);
*(&v24 + v6 + 1) ^= (byte_140003021[v6 - 2] - 12);
}
while ( v6 < 25 );
|
cs |
140003021에 있는 값을 가져와 초기화한 변수에 차례대로 xor한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
if ( v2 )
{
v8 = 0;
v9 = &v25;
do
{
v10 = *v9;
++v9;
v11 = v8++ + (v10 ^ 0xF);
*(v9 - 1) = v11;
}
while ( v8 < 25 );
v50 = 0;
wprintf(L"%s\n", &v25);
}
|
cs |
v2 가 1이면, 즉 아까 숫자들을 다 맞췄으면 아까 변수들에 0xf 을 xor 해서
출력해준다. 이것이 flag 이다.
이 문제는 아주 쉽다. 우리의 입력값들이 flag 를 만들어내는데 아무런 영향을 주지 않고,
x96 dbg 로 6개 변수값을 확인할 수 있으니, 값들을 맞출 수도있고, 그냥 뛰어넘고 flag 를 generate 할 수 있다.
그리고 v2 를 조작하거나 분기하지 않도록 조작해서 flag 를 출력할 수 있다.
entrypoint 까지 F9 로 실행하고, 현재모듈에서 cls 라는 문자열을 찾았다. 여기가 wmain 인 것같다.
이 부분에 브포를 걸고, F9 를 눌러 아무숫자 6개를 입력한다.
브포에 도착하면, ZF 을 0으로 만들어 분기를 조작한다.
놀랍게도 v2 는 R8 레지스터였다. R8 을 1로 만든 뒤, F9 를 눌러 실행하면 flag 가 나온다~
wprintf 에 브포 안걸고 실행하면 flag 확인 못하고 종료되는거 아시죵?