Keygen 문제인데, User에 입력할 값은 주어져 있다.
Form은 이렇게 생겼다. 총 5개의 Text Field를 채워야 한다.
예를 들어 아래와 같이 입력된 경우 오른편의 메시지 박스가 나타난다.
PEiD로 확인했더니 C#이다. 웬만해선 쉽게 풀 수 있을 것 같다.
Button1_Click이라는 이벤트 처리 메소드에서 Checkyn이라는 메소드 결과를 이용해 분기 작업을 수행한다.
Checkyn 메소드의 내용은 다음과 같다.
처음에 각 입력값에 대한 길이가 15를 넘지 않으면 X라는 문자로 패딩작업을 한다.
그렇게 16자리 문자열을 만들어낸 후, 일종의 key값인 A!<>FWRSGT#$324A를 오름차순 정렬한 문자열을 이용해 XOR, modular 연산을 거친 결과를 누적시켜 수를 만들어낸다.
두 입력값의 상기 설명한 연산 결과의 차이가 3이 되어야 결과로 True를 볼 수 있다.
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 | private bool Checkyn(string user, string serial) { int length1 = user.Length; while (length1 <= 15) { user += "X"; checked { ++length1; } } int length2 = serial.Length; while (length2 <= 15) { serial += "X"; checked { ++length2; } } string str = "A!<>FWRSGT#$324A"; int num1 = 0; int num2 = 0; int num3 = checked (str.Length - 1); int index1 = num2; while (index1 <= num3) { checked { num1 += Strings.Asc(str[index1]) ^ unchecked (Strings.Asc(user[index1]) % checked (index1 + 1)); } checked { ++index1; } } int num4 = checked (num1 + 3); int num5 = 0; int num6 = 0; int num7 = checked (str.Length - 1); int index2 = num6; while (index2 <= num7) { checked { num5 += Strings.Asc(str[index2]) ^ unchecked (Strings.Asc(serial[index2]) % checked (index2 + 1)); } checked { ++index2; } } return num4 == num5; } | cs |
Checkyn을 Python에서 그대로 구현한 후, brute forcing을 위한 코드도 추가하였다.
나름 선형화하여 처음에는 X가 15개일 때부터 즉, 총 62개(대소문자+숫자)의 입력 가능 경우의 수 중 답이 있는지 확인한다.
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 47 48 49 50 51 52 | #-*- coding: utf-8 -*- import os import string def Checkyn(user, serial=None): length1 = len(user) while length1 <= 15: user += "X" length1 += 1 length2 = len(serial) while length2 <= 15: serial += "X" length2 += 1 str = "A!<>FWRSGT#$324A" num1 = index1 = 0 num3 = len(str) - 1 while index1 <= num3: num1 += ord(''.join(sorted(list(str)))[index1]) ^ ord(''.join(sorted(list(user)))[index1]) % (index1 + 1) print "(str[i], user[i], str[i]^user[i], i) -->", print (ord(''.join(sorted(list(str)))[index1]), ord(''.join(sorted(list(user)))[index1]), ord(''.join(sorted(list(str)))[index1]) ^ ord(''.join(sorted(list(user)))[index1]), (index1 + 1), num1) index1 += 1 num5 = index2 = 0 num7 = len(str) - 1 while index2 <= num7: num5 += ord(''.join(sorted(list(str)))[index2]) ^ ord(''.join(sorted(list(serial)))[index2]) % (index2 + 1) print "(str[i], serial[i], str[i]^serial[i], i) -->", print (ord(''.join(sorted(list(str)))[index2]), ord(''.join(sorted(list(serial)))[index2]), ord(''.join(sorted(list(str)))[index2]) ^ ord(''.join(sorted(list(serial)))[index2]), (index2 + 1), num5) index2 += 1 return (num1 + 3), num5, (num1 + 3) == num5 def brute_force_serial_for_checkyn(user): # Padding 문자인 X가 15개일 때부터 시도, 하나도 없으면 점점 X의 수를 감소시킴 letters = string.letters + string.digits for char in letters: if Checkyn(user, "X"*15 + char)[2] == True: print Checkyn(user, "X"*15 + char) break return "XXXX-"*3 + "XXX" + char def main(): os.system("cls") # print Checkyn("Trythis0ne", "X"*15+"b") print "\nAnswer: " + brute_force_serial_for_checkyn("Trythis0ne") if __name__ == '__main__': main() | cs |
결과는 다음과 같이 나타난다.
답을 찾을 경우 마지막에 Answer: 이후에 나타난다.
X 15개일 때 답이 하나 나왔으므로, 맞는지 확인해보면 맞다는 것을 알 수 있다.
어차피 정렬한 후 연산하므로 16글자의 순서는 상관없을 것 같았는데, 실제 순서를 바꿔 입력해 보면, 성공 메시지가 나타나지 않는다.
C#의 Strings.Asc는 Python의 sorted와 달리 ASCII 코드를 기준으로 하는 것이 아니라 말그대로 대소문자 상관없이 알파벳 순으로 정렬하는 것 같다.
즉, XXXXXXXXXXXXXXXA를 정렬한 것과 XXXXXXXXXXXXXXXa를 정렬한 것이 모두 A나 a를 맨 앞으로 보낸 결과가 될 것이라는 추측을 해 본다.
답이 여럿 나올 수 있겠지만, 일단 하나 건졌으니 입력해보자.
인증키를 얻을 수 있다.
'[Wargame Write-up] > TryThis0ne' 카테고리의 다른 글
[TryThis0ne] [Application] TT0 - KeyGenMe v2.0 (0) | 2017.02.07 |
---|---|
[TryThisOne] [Application] Zip me if you can (0) | 2017.02.05 |
[TryThisOne] [Cryptography] Reverse Me (0) | 2017.01.02 |
[TryThisOne] [Realistic] RootConsole (0) | 2016.12.17 |
[TryThisOne] [Application] TT0 CrackMe 4 (0) | 2016.12.09 |