[CodeEngn Basic Final]

2019. 1. 31. 07:160x02 Reverse Engineer/0x01. CodeEngn

728x90

2N이라는 동생이 코드 피드백을 해달라고 해서 문제가 기억이 안나서 다시 풀어보았다..


문제 설명 
이 프로그램은 Key파일을 필요로 하는 프로그램이다. 
문구가 출력되도록 하려면 crackme3.key 파일안의 데이터는 무엇이 되어야 하는가 
문구 : Cracked by : CodeEngn!"

문제 컨셉 :  KEY 값을  통해 바이너리에서 메시지박스를 출력시킬 수 있는가?

  1. CRACKME3.KEY를 생성해두어야 한다 
이유 : OPEN_EXISTING(3) 

  1. 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