sweetchip's blog


안녕하세요 pgnsc입니다.


엣날에 인터넷을 돌아다니다가 2011 순천향대 문제를 발견했습니다.


언제 한번 풀어볼까.. 하다가 바로 오늘 풀어보게 되었습니다.



독수리.zip


위 독수리 파일안에 하나의 그림파일과 하나의 실행파일이 있습니다.


그림파일의 조건에 맞춰서 패스워드를 알아내는 것입니다.




[잘 안보이시면 확대해서 보세요 - exe와 첨부된 문제파일입니다.]


이부분에서 주목할것은 가운데 프로그램과 오른쪽 아래의 시간입니다. [왜그런지는 이따가 나옵니다.]




일단 이 프로그램은 영어로만 이루어진 문자를 넣으면 암호화 시켜서


암호문을 출력시킵니다.


그러면 위 사진에서 I am password 로 가려진 부분이 실제로 정답일 것이고,


아래 부분이 암호화된 부분일겁니다.


한마디로, 암호화루틴을 알아내고 다시 복호화 시키는것이 정답인 복잡한 문제입니다 ㅠㅠㅠ


일단 올리디버거로 살펴보도록 하겠습니다.



어느떄와 마찬가지로 주석을 달아뒀습니다.


getwindowtextA 에서 string 을 받아오는것부터 시작입니다.


00D71187 . 8B3D 6020D700 MOV EDI,DWORD PTR DS:[<&MSVCR100.iswalph>; MSVCR100.iswalpha

00D7118D . 8D75 E0 LEA ESI,DWORD PTR SS:[EBP-20]

00D71190 > /66:0FBE16 MOVSX DX,BYTE PTR DS:[ESI]

00D71194 . |0FB7C2 MOVZX EAX,DX

00D71197 . |50 PUSH EAX

00D71198 . |FFD7 CALL EDI // EDI는 00D71187 에서 넣어준 iswalpha 의 주소값.

00D7119A . |83C4 04 ADD ESP,4

00D7119D . |85C0 TEST EAX,EAX

00D7119F . |74 2F JE SHORT ReverseM.00D711D0

00D711A1 . |46 INC ESI

00D711A2 . |803E 00 CMP BYTE PTR DS:[ESI],0

00D711A5 .^\75 E9 JNZ SHORT ReverseM.00D71190 ; 알파벳 확인 루틴


알파벳이 아닌경우 use only alphabet 으로 나타냅니다.


모두다 알파벳인경우 [대문자 소문자 상관없더라구요.]


다음 루틴으로 넘어갑니다.


00D711AC . /74 42 JE SHORT ReverseM.00D711F0

00D711AE . |8BFF MOV EDI,EDI

00D711B0 > |0FBE541D E0 MOVSX EDX,BYTE PTR SS:[EBP EBX-20] // 입력한 글자를 한글자씩 가져옵니다.

00D711B5 . |8D441A 01 LEA EAX,DWORD PTR DS:[EDX EBX 1] // 한글자씩 가져온 문자에 현재 루프횟수와 1을 더합니다.

00D711B9 . |50 PUSH EAX ; /c

00D711BA . |FF15 6C20D700 CALL DWORD PTR DS:[<&MSVCR100.isalpha>] ; \isalpha //00D711B5의 값을 알파벳 검사를 합니다.

00D711C0 . |83C4 04 ADD ESP,4

00D711C3 . |85C0 TEST EAX,EAX

00D711C5 . |74 1A JE SHORT ReverseM.00D711E1

00D711C7 . |8D4B 01 LEA ECX,DWORD PTR DS:[EBX 1] // 알파벳인 경우 루프횟수 0x01

00D711CA . |004C1D E0 ADD BYTE PTR SS:[EBP EBX-20],CL

00D711CE . |EB 18 JMP SHORT ReverseM.00D711E8

00D711D0 > |8B0D 7433D700 MOV ECX,DWORD PTR DS:[D73374]

00D711D6 . |68 B021D700 PUSH ReverseM.00D721B0 ; ASCII "Use Only Alphabet!"

00D711DB . |51 PUSH ECX

00D711DC . |E9 E2010000 JMP ReverseM.00D713C3

00D711E1 > |8D53 E7 LEA EDX,DWORD PTR DS:[EBX-19] // 알파벳이 아닌경우 루프횟수 - 0x19

00D711E4 . |00541D E0 ADD BYTE PTR SS:[EBP EBX-20],DL

00D711E8 > |43 INC EBX

00D711E9 . |807C1D E0 00 CMP BYTE PTR SS:[EBP EBX-20],0

00D711EE .^|75 C0 JNZ SHORT ReverseM.00D711B0 ;

00D711F0 > \8D45 E0 LEA EAX,DWORD PTR SS:[EBP-20]


현재글자 루프횟수 1 이후 알파벳 검사하는 루틴입니다.


마찬가지로 알파벳이 아닌경우 use only alphabet 으로 나타냅니다.


그 다음



00D71210 > 8A5C05 E0 MOV BL,BYTE PTR SS:[EBP EAX-20]

00D71214 . |8A540D E0 MOV DL,BYTE PTR SS:[EBP ECX-20]

00D71218 . |885C0D E0 MOV BYTE PTR SS:[EBP ECX-20],BL

00D7121C . |885405 E0 MOV BYTE PTR SS:[EBP EAX-20],DL

00D71220 . |41 INC ECX

00D71221 . |48 DEC EAX

00D71222 . |3BC8 CMP ECX,EAX

00D71224 .^\7C EA JL SHORT ReverseM.00D71210


위에서 나온 결과를 거꾸로 뒤집는 루틴입니다.


간단하니 따로 해석은 안하겠습니다 :D




그 아래 00D71226 ~ 00D1367 까지는 타임테이블을 생성하는 부분입니다.


아까 시간을 잘 보라고 한 이유가 이것입니다.


타임 테이블을 구한다는 것은 시간마다 답이 바뀐다는 것이며,


사진상의 답을 구하려면 사진에 찍힌 날자를 알아야 구할수 있다는 것입니다.



타임 테이블 생성 종료!



스택의 주소 0046F4D8 ~ 0046F4F8 까지가 타임 테이블의 값입니다


타임테이블은 현재 시간으로 구성됩니다. 헥스 덤프를 보시면 아시듯이 2012 1126 1208 입니다.

2012 년 11월 26일 12시 08분.. 초는 안나오네요 :D


자, 다음 루틴.


00D7136D . 8D75 E0 LEA ESI,DWORD PTR SS:[EBP-20] ;

00D71370 > /83FF 0C CMP EDI,0C ; ///////////////////////////////////

00D71373 . |75 02 JNZ SHORT ReverseM.00D71377

00D71375 . |33FF XOR EDI,EDI

00D71377 > |8B44BD B0 MOV EAX,DWORD PTR SS:[EBP EDI*4-50] // 타임테이블을 한글자씩 가져옵니다.

00D7137B . |83F8 1A CMP EAX,1A

00D7137E . |7E 0C JLE SHORT ReverseM.00D7138C

00D71380 > |83E8 1A SUB EAX,1A // 타임테이블 값이 1A보다 큰경우 0x1A를 빼줍니다.

00D71383 . |8944BD B0 MOV DWORD PTR SS:[EBP EDI*4-50],EAX

00D71387 . |83F8 1A CMP EAX,1A

00D7138A .^|7F F4 JG SHORT ReverseM.00D71380 // 1A보다 큰경우 반복합니다. [00D71380 으로 이동]

00D7138C > |0FBE0E MOVSX ECX,BYTE PTR DS:[ESI] // 암호화 중인 글자를 한글자씩 가져옵니다.

00D7138F . |034CBD B0 ADD ECX,DWORD PTR SS:[EBP EDI*4-50] // 그 글자에 타임테이블 값을 더합니다.

00D71393 . |51 PUSH ECX ; /c

00D71394 . |FF15 6C20D700 CALL DWORD PTR DS:[<&MSVCR100.isalpha>] ; \isalpha //00D7138F의 값이 알파벳인지 확인하고

00D7139A . |83C4 04 ADD ESP,4

00D7139D . |85C0 TEST EAX,EAX

00D7139F . |74 08 JE SHORT ReverseM.00D713A9

00D713A1 . |8A54BD B0 MOV DL,BYTE PTR SS:[EBP EDI*4-50] // 알파벳인경우 글자 타임테이블 연산을 합니다.

00D713A5 . |0016 ADD BYTE PTR DS:[ESI],DL

00D713A7 . |EB 08 JMP SHORT ReverseM.00D713B1

00D713A9 > |8A44BD B0 MOV AL,BYTE PTR SS:[EBP EDI*4-50] // 알파벳이 아닌경우 글자 타임테이블을 연산합니다.

00D713AD . |2C 1A SUB AL,1A // 그리고 00D713A9에서 연산한 값에서 0x1A를 빼줍니다.

00D713AF . |0006 ADD BYTE PTR DS:[ESI],AL

00D713B1 > |46 INC ESI

00D713B2 . |47 INC EDI

00D713B3 . |803E 00 CMP BYTE PTR DS:[ESI],0

00D713B6 .^\75 B8 JNZ SHORT ReverseM.00D71370 ; ///////////////////////


대망의 마지막 루틴입니다.


위 주석처럼 연산을 하고 암호화를 종료합니다.



이로써 기나긴 암호화 루틴이 종료 되었습니다.


이제 매번 포스팅처럼 해왔던 키젠을 만들어야 겠군요.


포스팅을 쓸때는 모두다 만들어 놓고 포스팅을 합니다.


그래서 만들던중 위 알고리즘을 그대로 커스터 마이징 시켜서 프로그래밍 했는데,


프로그래밍 도중에 '아 나는 지금 암호화 시키는 것을 프로그래밍 하는구나' 라는것을 깨달았습니다 --


답을 구하려면 복호화 시켜야 하는데 암호화 시키는 것을 만들고 있던 셈이죠.


그래서 그냥 뭐 연습삼아서 만든다라고 생각하고.. :( [실전에서 이러면 큰일나죠]


일단.. 완성은 시키긴 했습니다.


만드는 도중 기왕에 제대로 만들자 해서 문제 프로그램과 완전 똑같이 구현했습니다.


물론 도스창과 폼창의 차이가 있지만요 ^^




순천향대 2011 W Keygen made by sweetchip.exe


Keygen source

// 퍼갈시 출처 남겨주세요~ :D

#include

#include

#include


void reverseString(char* s) {

size_t size = strlen(s);

char temp;


for (size_t i = 0; i < size / 2; i ) {

temp = s[i];

s[i] = s[(size - 1) - i];

s[(size - 1) - i] = temp;

}

}


int main()

{

time_t timer;

struct tm *t;

timer = time(NULL);

t = localtime(&timer);


char timetmp[15];

int time[12];


sprintf(timetmp,"%d%d%d%d%d", t->tm_year 1900, t->tm_mon 1, t->tm_mday, t->tm_hour,t->tm_min);


for(int i = 0; timetmp[i] != 0; i )

{

time[i] = timetmp[i] - '0';

}

puts("///////////////////////////////////////////////////");

puts("///// 순천향대 2011 정보보호 페스티벌 /////");

puts("///// W번 리버스 엔지니어링 Keygen /////");

puts("///// [En] Made By sweetchip /////");

puts("///////////////////////////////////////////////////");

char name[0x100];

int timelen = sizeof(time) / sizeof(time[0]);


printf("\n현재시간은 %s 입니다\n\n",timetmp);

printf("Input your name : ");


gets(name);

int namelen = strlen(name);


for(int i = 0; i

{

if(!isalpha(name[i]))

{

printf("Use Only Alphabet.");

return 0;

}

}


for(int i = 0; i

{

char alpha= name[i];

alpha = name[i] i 0x01;


if(!isalpha(alpha))

{

name[i] = name[i] i - 0x19;

}

else

{

name[i] = name[i] i 0x01;

}

}


reverseString(name);


for(int i = 0,j = 0; i < namelen; i ,j )

{

if(timelen == j)

{

j=0;

}


char tmp;

char tmp1;

if(time[j]<=0x1A)

{

tmp = name[i] time[j];

if(isalpha(tmp))

{

name[i] = name[i] time[j];

}

else

{

name[i] = name[i] (time[j] - 0x1A);

}

}

else

{

while(1)

{

time[j] = time[j] - 0x1A;

if(time[j]<=0x1A)

{

break;

}

}

}


}

printf("Encodeing . . . \nYour Key is : %s\n\n",name);

system("pause");

}


복호화 키젠은 다음 포스팅에.. ^^

신고

댓글 0