안녕하세요 pgnsc입니다.
엣날에 인터넷을 돌아다니다가 2011 순천향대 문제를 발견했습니다.
언제 한번 풀어볼까.. 하다가 바로 오늘 풀어보게 되었습니다.
위 독수리 파일안에 하나의 그림파일과 하나의 실행파일이 있습니다.
그림파일의 조건에 맞춰서 패스워드를 알아내는 것입니다.
[잘 안보이시면 확대해서 보세요 - 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"); }
복호화 키젠은 다음 포스팅에.. ^^
Secure Korea 2011 Reversse Engineering 문제 풀이 (0) | 2012.12.09 |
---|---|
순천향대 정보보호 페스티벌 2011 W번 문제풀이[Decrypter] (0) | 2012.11.28 |
Babylon's KeygenMe - 풀이 및 Keygen source. (0) | 2012.11.25 |
이름모를 Crackme 풀이 키젠포함 (0) | 2012.11.24 |
Duelist's Crackme 2 Keyfile (0) | 2012.11.21 |