Jaeseo's Information Security Story

Reversing.kr 2번 Easy Keygen - WriteUp 본문

Write UP/Reversing.kr

Reversing.kr 2번 Easy Keygen - WriteUp

Jaeseokim 2019. 11. 10. 18:50

Reversing.kr의 Easy Keygen 문제 입니다.

일단 문제를 다운 받으면 ReadMe.txt 파일과 Easy Keygen.exe 파일이 있습니다.

일단 ReadMe.txt 파일을 읽으면 내용은 아래와 같습니다.

ReversingKr KeygenMe


Find the Name when the Serial is 5B134977135E7D13

문제를 보니 시리얼 5B134977135E7D13이 나올때의 이름을 찾으라는 문제입니다.

일단 pe 분석을 해서 패킹이 되어 있는 지를 확인 해봅니다.

C++로 짜여 있고 Packing과 같은 조치는 안되어 있는 것으로 보입니다.

이제 프로그램을 실행해보고 IDA로 정적 분석을 해봅니다.

매우 간단한 프로그램으로 보입니다. name을 입력하면 name가지고 알고리즘을 통해 keygen을 하고 입력된 serial과 비교하는 프로그램으로 보입니다.

이제 이러한 keygen 알고리즘을 파악 하기 위해 ida로 열어봅니다.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  signed int v3; // ebp
  signed int i; // esi
  char v6; // [esp+Ch] [ebp-130h]
  char v7; // [esp+Dh] [ebp-12Fh]
  char v8; // [esp+Eh] [ebp-12Eh]
  char v9; // [esp+10h] [ebp-12Ch]
  char v10; // [esp+11h] [ebp-12Bh]
  __int16 v11; // [esp+71h] [ebp-CBh]
  char v12; // [esp+73h] [ebp-C9h]
  char v13; // [esp+74h] [ebp-C8h]
  char v14; // [esp+75h] [ebp-C7h]
  __int16 v15; // [esp+139h] [ebp-3h]
  char v16; // [esp+13Bh] [ebp-1h]

  v9 = 0;
  v13 = 0;
  memset(&v10, 0, 0x60u);
  v11 = 0;
  v12 = 0;
  memset(&v14, 0, 0xC4u);
  v15 = 0;
  v16 = 0;
  v6 = 16;
  v7 = 32;
  v8 = 48;
  sub_4011B9(aInputName);
  scanf(aS, &v9); // as == "%s" 즉 name 값을 v9에 저장
  v3 = 0; 
  for ( i = 0; v3 < (signed int)strlen(&v9); ++i ) // keygen을 위한 알고리즘
  {
    if ( i >= 3 ) // 
      i = 0;
    sprintf(&v13, aS02x, &v13, *(&v9 + v3++) ^ *(&v6 + i)); 
  } // 알고리즘 처리후 생성된 serial를 v13에 저장
  memset(&v9, 0, 0x64u);
  sub_4011B9(aInputSerial);
  scanf(aS, &v9); // v9에 serial 값을 저장
  if ( !strcmp(&v9, &v13) ) // strcmp를 통해 입력값과 keygen으로 생성된 serial값과 비교
    sub_4011B9(aCorrect);
  else
    sub_4011B9(aWrong);
  return 0;
}

대략적으로 파악을 해보면 위와 같은 구조로 보입니다.

이때 keygen을 하는 알고리즘을 다시한번더 분석해봅니다.

char v6 = 16; // 이해하기 쉽게 코드를 수정 했습니다.
char v7 = 32;
char v8 = 48;
char v9 = 0;
printf("aInputName"); // aInputName 출력 
scanf("%s", &v9); // scanf를 통해 name을 입력 받음
int v3 = 0; // v3 초기화
for ( int i = 0; v3 < (signed int)strlen(&v9); ++i )
//v3보다 v9의 strlen이 더클동안 반복
{ // i를 0으로 초기화 하고 반복 할때마다 i를 1씩 증가
  if ( i >= 3 ) // i가 2보다 커지면 i를 0으로 초기화 
    i = 0;
  sprintf(&v13, "%s%02X", &v13, *(&v9 + v3++) ^ *(&v6 + i));
  //sprintf를 통해 v13에 생성된 serial 저장
  //"%s%02X"을 통해 기존 v13 내용 + (v9 포인터 + v3++(즉 반복하면서 입력값 한글자씩 가져옴)와 
  //xor 연산을 하는데 대상은 반복하면서 차례대로 v6,v7,v8과 하는 것을 볼수 있다.
}

알고리즘을 분석해보면 위와 같은 내용이 나오는데 이것을 가지고 복호화 시키는 알고리즘을 짜봅니다.

key = 16 # 복호화에 사용되는 키값
serial = "5B134977135E7D13" # 시리얼 값 
name = "" # name을 저장 하기 위한 변수 선언
k = 1 # key*k를 위한 변수
for i, j in zip(serial[::2], serial[1::2]):
# serial에서 2글자씩 분리해서 i와 j에 저장
    if k > 3: # key*k를 위한 3이상되면 1로 초기화
        k = 1
    serial_h = i+j # 분리된 i,j를 합쳐줌
    name += chr(int(serial_h, 16) ^ key*k) # 2글자를 가져와 16,32,48과 차례대로 xor연산을 하고 char형태로 바꿔 name에 저장
    k = k+1 # k++!
print(name) # flag 출력!

위의 스크립트를 통해 복호화를 하면 사용자의 name를 구할수가 있습니다.

 

AUTH : K3yg3nm3

Comments