접기
프로그램을 시작하면 위와 같이 출력된다.
키젠미의 전형적인 유형이다.
IDA를 통해 분석해보면 keygen() 함수(0x401146)에서 Name을 통해 Serial을 생성한 후, 13번 째 줄 아래의 루틴에서 사용자가 입력한 Serial과 Name을 통해 생성된 Serial을 비교하는 것을 알 수 있다.
keygen() 함수 내부를 보면 위와 같이 조건문이 가득한 것을 볼 수 있다. ollydbg를 통해 보면 더 헬이다 슈발.
그리고 저 조건문에서 비교하는 저 한 바이트짜리 값들은 이 프로그램 내에서 하나의 명령어로 작동한다.
위처럼 저장되어 있는데, 어떠한 루틴에 따라 해당 바이트가 선택되서 그 바이트마다의 명령이 각각 실행되는 것이다.
내가 시발 글로 써도 뭔 말인지 설명을 잘 못 하겠는데, 직접 5분 이상 분석해보면 무슨 말인지 알 것이다.
예를들어 0x5C라는 바이트 값은 swap 역할을 하는 코드이다.
어쨋든 저 ㅈㄴ 많은 조건문들은 핵 암이다.
분기문 중에서도 "sub_4013BF"와, "sub_4013AB"를 자주 호출하는 것을 볼 수 있는데, 이 두 함수들이 이 문제의 가장 중요한 요소이다.
이 두 함수들은 각각 자료구조 스택(stack)에서의 pop과 push의 역할을 한다.
pop과 push라는 것을 눈치채면 이 문제를 절반 이상 풀었다고 볼 수 있다. 이 부분만 이해하면 시발 프로그램의 흐름이 보이기 때문이다!!
나는 우선 Name을 가져오는 부분부터 시작해서 ㅈㄴ 삽질했다.
거의 하루를 다 써서 풀었다 ㅅㅂ.. ㅠㅜ
내가 앞에서 각 바이트 값별로 프로그램 내에서 하나의 명령을 수행한다고 했는데, 이 말과 push, pop을 눈치채면 프로그램의 흐름이 보인다.
결론부터 말하자면 keygen 흐름은 아래와 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void keygen() {
string name, serial;
unsigned int sum = 0x3b10 ;
unsigned int a, b;
cout < < "input name : " ;
cin > > name;
for (int i = 0 ; i < name.length(); i+ + )
sum = ((int )name[i] * 0x426c ) + sum;
printf ("sum : %X\n" , sum);
for (int i = 0 ; i < 8 ; i+ + ) {
a = sum / 0xf , b = sum % 0xf ;
b + = 0x30 ;
if (b > 0x39 ) b + = 0x8 ;
serial + = (char )b;
sum / = 0xf ;
sum * = 0x2 ;
}
cout < < "serial is : " < < serial < < endl ;
}
cs
그리고 여기서 코드를 거꾸로 짜서 serial에서 name을 뽑아낼 수 있도록 코딩해야 한다.
직접 해보면 알 수 있겠지만 그건 불가능한데, 위 코드에서 8~9번 째 줄에서 막히기 때문이다.
결과적으로 구할 수 있는 것은 각 Name의 값들을 다 더한 값만을 구할 수 있다.
Serial이 94E7DB1B일 때, Name의 sum 값은 0x3b10이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include < iostream>
#include < string >
#include < algorithm>
using namespace std ;
void keygen() {
string name, serial;
unsigned int sum = 0x3b10 ;
unsigned int a, b;
cout < < "input name : " ;
cin > > name;
for (int i = 0 ; i < name.length(); i+ + )
sum = ((int )name[i] * 0x426c ) + sum;
printf ("sum : %X\n" , sum);
for (int i = 0 ; i < 8 ; i+ + ) {
a = sum / 0xf , b = sum % 0xf ;
b + = 0x30 ;
if (b > 0x39 ) b + = 0x8 ;
serial + = (char )b;
sum / = 0xf ;
sum * = 0x2 ;
}
cout < < "serial is : " < < serial < < endl ;
}
void decode() {
string serial;
unsigned int a = 0 , b;
cout < < "input serial : " ;
serial = "94E7DB1B" ;
//cin >> serial;
while (serial.size ()) {
b = serial.back();
serial.pop_back();
b - = 0x30 ;
if (b > 0x9 ) b - = 8 ;
a = a * 0xf + b;
a / = 2 ;
}
a * = 2 ;
printf ("sum(name) = %X\n" , (a - 0x3b10 ) / 0x426c );
}
int main() {
//keygen();
decode();
return 0 ;
}
cs
이제 sum을 구했으니 다 더했을 때 sum이 나오는 Name을 구하면 된다.
접기 하 시발 점점 어려워진다. 이 한 문제에 이틀 가까이 걸렸다 ㅠㅜ
댓글