[CodeEngn Basic Final]
2019. 1. 31. 07:16ㆍ0x02 Reverse Engineer/0x01. CodeEngn
728x90
2N이라는 동생이 코드 피드백을 해달라고 해서 문제가 기억이 안나서 다시 풀어보았다..
문제 설명
이 프로그램은 Key파일을 필요로 하는 프로그램이다.
문구가 출력되도록 하려면 crackme3.key 파일안의 데이터는 무엇이 되어야 하는가
“문구 : Cracked by : CodeEngn!"
문제 컨셉 : KEY 값을 통해 바이너리에서 메시지박스를 출력시킬 수 있는가?
- CRACKME3.KEY를 생성해두어야 한다
이유 : OPEN_EXISTING(3)
- CRACKME3.KEY 내부 바이트는 18바이트가 되어야 한다.
18바이트가 아닐 경우, 프로그램은 종료된다.
초기화 작업을 거치고, .KEY에 있는 값[ESP+4]을 4바이트씩 ESI로 복사한다.
그 후 0x41값을 BL에 복사하게 되면 402041주소가 만들어진다 (그닥 중요한 부분은 아니다)
.KEY에 있는 값을 1바이트씩 AL로 복사한다.
복사 된 바이트 값과 0x41을 XOR 취한다.
xor된 값을 다시 파일에 복사한다.
INC ESI : .KEY파일의 값이 담겨 있는 주소가 ESI인데 +1 바이트 증가 시킨다는 의미다 즉, arr[0] -> arr[1] -> …. arr[17]
그 후, BL값이 증가됨을 볼 수 있는데 0x41 -> 0x42 …. -> 0x4F이렇게 된다는 것이다. (15바이트만 변경됨을 알 수 있다)
00401311 /$ 33C9 XOR ECX,ECX
00401313 |. 33C0 XOR EAX,EAX
00401315 |. 8B7424 04 MOV ESI,DWORD PTR SS:[ESP+4]
00401319 |. B3 41 MOV BL,41
0040131B |> 8A06 /MOV AL,BYTE PTR DS:[ESI]
0040131D |. 32C3 |XOR AL,BL
0040131F |. 8806 |MOV BYTE PTR DS:[ESI],AL
00401321 |. 46 |INC ESI
00401322 |. FEC3 |INC BL
00401324 |. 0105 F9204000 |ADD DWORD PTR DS:[4020F9],EAX
0040132A |. 3C 00 |CMP AL,0
0040132C |. 74 07 |JE SHORT 20.00401335
0040132E |. FEC1 |INC CL
00401330 |. 80FB 4F |CMP BL,4F
00401333 |.^75 E6 \JNZ SHORT 20.0040131B
만들어진 값에 xor 12345678을 한다.
00401079 |. 8135 F9204000 >XOR DWORD PTR DS:[4020F9],12345678
두 개의 값을 비교한다.
EAX는 입력해둔 [14] - [17] 이다.
00401093 |. 3B05 F9204000 CMP EAX,DWORD PTR DS:[4020F9]
[4020F9] = 0x12345151이다.
잠시 IDA로 와서 정적분석
if ( fd != (HANDLE)-1
&& (hFile = fd, ReadFile(fd, &data, 18u, &NumberOfBytesRead, 0), NumberOfBytesRead == 18)// 18바이트
&& (encrypt(&data), after_enc_data ^= 0x12345678u, (v2 = sub_40133C((int)&data) == dword_4020F9) != 0) )
통과 된 목록
fd != (HANDLE)-1
ReadFile(fd, &data, 18u, &NumberOfBytesRead, 0), NumberOfBytesRead == 18
강제로 실행해보면 이렇다.
왜 저런 값이 나왔을까?
내가 입력한 값이 aaaaaaaaaaaaaaQT4d였다.
Round 1 Code
#include <stdio.h>
char data[9] = "CodeEngn";
void encrypt()
{
static int x = 0x41;
for(int i=0; i<9; i++)
{
data[i] ^= x;
x++;
printf("%x",data[i]);
}
printf("\n");
}
int main(int argc, char *argv[])
{
encrypt();
}
위의 값을 파일에 옮긴 후 슥삭 슥삭 진행하면….잘 바꼈다. ^0^
하지만 이게 끝이 아니었다.
xor 12345678 부분이 있기에
00401079 |. 8135 F9204000 >XOR DWORD PTR DS:[4020F9],12345678
이 값이 만들어지는 원리를 살펴보아야 자동화 툴을 작성할 수 있다.
아래의 0X401074에서 호출 되는 Encrypt함수에 접근하기 전에는 0x4020F9 값이 0이다.
encrypt에 진입하면 다음과 같다.
키에 있는 값들을 1바이트씩 차곡 차곡 가져와서 xor BL한 후, 하위 1바이트는 [ESI]에 심어두고
EAX 전체는 4020F9주소에 연산하여 넣는다.
총 15번 돌면서 파일에서 xor 된 값을 자꾸 자꾸 더하여 0x4020F9에 넣게된다 .
이 까지 코드로 짜면 다음과 같다.
Round Code 2.
int hap = 0;
int BL = 0x41;
for(int i=0; i<15; i++)
{
ESI[i] ^= BL;
hap += ESI[i];
if(ESI[i] == 0)
break;
BL++;
}
아래의 코드는 풀이코드.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// ver1. only CodeEngn Script
// ver2. make 9bytes Script
// 9bytes(include null)
// Version 1 : HardCode is "CodeEngn"
char data[9] = "\x43\x6f\x64\x65\x45\x6e\x67\x6e";
// Version 2 : Automatic User name (limit 9 bytes)
// char data[9] = "\0";
//===================================================//
// 16bytes(include null)
// Version 1 : HardCode encrypt "CodeEngn"
//char data2[19] = "\x02\x2d\x27\x21\x00\x28\x20\x26\x49\x41\x41\x41\x41\x41\x41";
// Version 2 : Automatic Encrypt Data
char cp_data[18] = "\0";
char *file_data;
void encrypt()
{
static int x = 0x41;
printf("==============encrypt data ==============\n");
for(int i=0; i<sizeof(data)/sizeof(data[0]); i++)
{
data[i] ^= x;
x++;
printf("%02x ",data[i]);
}
// filter null this encrypt data use to 'null' so you can't use strcpy
// strcpy(cp_data,data);
printf("\n");
}
void copy_data()
{
printf("==========copy========\n");
for(int i=0; i<sizeof(data)/sizeof(data[0]); i++)
{
cp_data[i] = data[i];
printf("%02x ",cp_data[i]);
}
printf("\n======================\n");
}
void make_dummy()
{
printf("=========dummy generate=======\n");
for(int i=10 ; i<14; i++)
{
cp_data[i] = 'A';
}
printf("\n Result copy data \n");
for(int i=0; i<14; i++)
{
printf("%c ",cp_data[i]);
}
printf("\n");
}
void convert()
{
unsigned int _4020F9 = 0x00;
static int x = 0x41;
unsigned short n = 0;
for(int i=0; i<16 ; i++)
{
cp_data[i] ^= x;
if(cp_data[i] == 0x0)
break;
x++;
_4020F9 += cp_data[i];
}
printf("make dummy...\n");
make_dummy();
printf("\nbefore xor ==> %x\n",_4020F9);
_4020F9 ^= 0x12345678;
printf("0x4020F9 = %x\n",_4020F9);
printf("split..\n");
//[14], [15], [16], [17]
cp_data[14] = _4020F9&0xff;
printf("arr[3] = %x\n", _4020F9&0xff);
_4020F9 >>= 8;
cp_data[15] = _4020F9&0xff;
printf("arr[2] = %x\n", _4020F9&0xff);
_4020F9 >>= 8;
cp_data[16] = _4020F9;
printf("arr[1] = %x\n", _4020F9&0xff);
_4020F9 >>= 8;
cp_data[17] = _4020F9&0xff;
printf("arr[0] = %x\n", _4020F9&0xff);
printf("\n==============Result==================\n");
for(int i=0; i<sizeof(cp_data)/sizeof(cp_data[0]); i++)
{
printf("%02x ",cp_data[i]);
}
printf("\n============================================\n");
}
// append Version 2.
void filter(char data[])
{
int len = strlen(data);
if(len != 8)
{
printf("bye\n");
}
}
int main(int argc, char *argv[])
{
//filter(argv[1]);
encrypt();
copy_data();
convert();
}
encrypt 에서 convert 되기 전 값 + 더미 + xor && plus 연산 결과 값
KEY파일에 들어갈 18바이트
02 2d 27 21 00 28 20 26 49 00 41 41 41 41 7b 55 34 12
'0x02 Reverse Engineer > 0x01. CodeEngn' 카테고리의 다른 글
[CodeEngn basic 18] (0) | 2018.02.09 |
---|---|
[CodeEngn Basic 17] (0) | 2018.02.09 |
[CodeEngn Basic 16] (0) | 2018.02.09 |
Basic 15번. (0) | 2018.02.09 |
[CodeEngn Basic 15] obj. KNOW BOLRAND & Routine (0) | 2017.08.23 |