Сейчас я Вам расскажу про то как был решен вот этот кейген – http://forum.reverse4you.org/showthread.php?t=1421.
Собвственно, запустим кейген, введем какие-то данные и запустим. Если вы не угадали серийник ( ;) ), то у вас выскочит сообщение вида: «Wrong serial». Его мы и попробуем пожалуй найти. Открыв окошко со строками в IDA ( Shift + F12 ) получил следующие:
Перейдем на .data:0040322B и посмотрим в какой функции есть ссылка на эту строку:Code:.rdata:004020BA 0000000B C user32.dll .rdata:004020E8 0000000D C kernel32.dll .data:00403221 0000000A C Good job! .data:0040322B 0000000D C Wrong serial
Видим, что эта строка используется в функции sub_40112E. Вероятно там и происходит все, что нас интересует! Давайте теперь рассмотрим эту функцию ( к сожалению, некоторые функции у меня переимнованы были в процессе анализа, так что бы стало удобней читать, некоторые из них я рассмотрю подробнее и поясню почему я так назвал):Code:.data:0040322B aWrongSerial db 'Wrong serial',0 ; DATA XREF: sub_40112E+C0#o
Сразу хочется рассмотреть самый первую функцию, что вызывается в функции sub_40112E. Я думаю, если мы туда перейдем и немножко посмотрим на нее, то думаю ни у кого не останется сомнений, что эта функция узнает длину строки. Там все достаточно очевидно, если нет, то отпишитесь кому не понятно я разберу и ее.Code:.text:0040112E sub_40112E proc near ; CODE XREF: DialogFunc+43#p .text:0040112E push offset String .text:00401133 call stringLength .text:00401138 cmp eax, 5 .text:0040113B jl loc_4011EA .text:00401141 mov ecx, 2 .text:00401146 cdq .text:00401147 div ecx .text:00401149 test edx, edx .text:0040114B jnz loc_4011EA .text:00401151 mul ecx .text:00401153 push eax .text:00401154 push offset String .text:00401159 call modifiengLogin .text:0040115E mov dword_403208, eax .text:00401163 push offset byte_403100 .text:00401168 call stringLength .text:0040116D cmp eax, 11h .text:00401170 jnz short loc_4011EA .text:00401172 mov eax, offset byte_403100 .text:00401177 mov byte ptr [eax+8], 0 .text:0040117B mov edi, offset byte_403100 .text:00401180 push edi .text:00401181 call modifiengSerial .text:00401186 mov esi, eax .text:00401188 mov dword_403200, eax .text:0040118D add edi, 9 .text:00401190 push edi .text:00401191 call modifiengSerial .text:00401196 mov edi, eax .text:00401198 mov dword_403210, eax .text:0040119D mov ecx, esi .text:0040119F call sub_4010F3 .text:004011A4 test ecx, ecx .text:004011A6 jnz short loc_4011EA .text:004011A8 mov ecx, edi .text:004011AA call sub_4010F3 .text:004011AF test ecx, ecx .text:004011B1 jnz short loc_4011EA .text:004011B3 mov eax, dword_403208 .text:004011B8 add eax, dword_403218 .text:004011BE xor dword_403200, eax .text:004011C4 jnz short loc_4011EA .text:004011C6 mov ecx, 2 .text:004011CB cdq .text:004011CC mul ecx .text:004011CE xor dword_403210, eax .text:004011D4 jnz short loc_4011EA .text:004011D6 push 0 ; uType .text:004011D8 push 0 ; lpCaption .text:004011DA push offset Text ; "Good job!" .text:004011DF push 0 ; hWnd .text:004011E1 call MessageBoxA .text:004011E6 xor eax, eax .text:004011E8 inc eax .text:004011E9 retn .text:004011EA ; --------------------------------------------------------------------------- .text:004011EA .text:004011EA loc_4011EA: ; CODE XREF: sub_40112E+D#j .text:004011EA ; sub_40112E+1D#j ... .text:004011EA push 0 ; uType .text:004011EC push 0 ; lpCaption .text:004011EE push offset aWrongSerial ; "Wrong serial" .text:004011F3 push 0 ; hWnd .text:004011F5 call MessageBoxA .text:004011FA xor eax, eax .text:004011FC retn .text:004011FC sub_40112E endp
Отметим, что строка для которой ищется длина строки — это наш введенный логин. Дальше наш введеный логин передается в функцию, которая рассчитывает длину строки, и далее идет проверка, что введенный логин больше 5. Если он меньше, то мы сразу получаем вывод сообщения «Wrong serial». Если длина логина больше 5, то далее идет достаточно забаный кусок, который в целом мне не понятен, либо я не правильно понял, что он делает...:
Тут происходит проверка, что длина введеного логина является четной, если она нечетная, то мы переходим на ветку, где у будет выведено окошко со строкой «Wrong serial».Code:.text:00401141 mov ecx, 2 .text:00401146 cdq .text:00401147 div ecx .text:00401149 test edx, edx .text:0040114B jnz loc_4011EA
Дальше у нас запихивается в стек длина логина и сам логин, и это все передается функции, которая делает какие-то преобразования ( рассмотрим их позже ) над нашим логином.
После этого мы переходим к нашему серийному номеру. Высчитываем его длину, сравниваем с 0х11. Соотвественно, если его длина меньше 17 символов, то мы увидим окошко с сообщением «wrong serial». В противном случаи рассматриваем код, который идет дальше:
Здесь происходит разбивка введеного серийного номера по сути на 2 части ( на 9ю позицию ставиться 0). Далее по сути береться первая часть введеного серийника и передается в функцию, которая что-то делает с ним ( рассмотрим ее позже ). Затем береться вторая часть серийного номера и передается в эту же функцию. Дальше для меня был немного не понятный момент, но , собственно, он мне ни на что не повлиял =). То что мы получили в результате преобразования над первой частью серийника передается в функцию sub_4010F3, которая проверяет, что бы никакой байт преобразованого серийника не была равной 0, т.*е. Допустим у нас есть строка вида 2233004, тогда функция будет работать так:Code:.text:00401172 mov eax, offset byte_403100 .text:00401177 mov byte ptr [eax+8], 0 .text:0040117B mov edi, offset byte_403100 .text:00401180 push edi .text:00401181 call modifiengSerial .text:00401186 mov esi, eax .text:00401188 mov dword_403200, eax .text:0040118D add edi, 9 .text:00401190 push edi .text:00401191 call modifiengSerial .text:00401196 mov edi, eax .text:00401198 mov dword_403210, eax .text:0040119D mov ecx, esi .text:0040119F call sub_4010F3 .text:004011A4 test ecx, ecx .text:004011A6 jnz short loc_4011E
1. Береться последний байт, т.*е. 04.
2. Проверяется не равен ли этот байт 0.
3. Если равен 0, то функция возвращает 1, что есть плохо. В этом случаи мы увидим окошко «Wrong serial»
4. Если не равен, то сдвигается строка на 4, т.*е. Получим уже вот такую строку 223300.
5. Переход к пункту 1. Это будет повторяться пока мы не пройдем всю строку, либо пока не встреим байт равный 0.
После вот этих всех мутных проверок идет достаточно важный кусок:
В еах помещается результат модификации логина, к нему прибавляется значение dword_403218 = DDBBCCAA ( при написании кейгена надо не забывать это константу прибовлять ). И в конце концов проверяется, что равна ли первая часть серийника вышеуказанному результату(назовем его result1). Если да, то переходи к проверке второй части серийника:Code:.text:004011B3 mov eax, dword_403208 .text:004011B8 add eax, dword_403218 .text:004011BE xor dword_403200, eax .text:004011C4 jnz short loc_4011EA
Тут происходит следубщие берем 2, расширяем его до 4 байт ( смотрим как работает cdq) и умножаем result1 на 2 и то, что получим (назовем result2) со второй модифицированной частью введенного серийника. И если они равны, то мы видим, что мы успешно подобрали=), если нет, то опять окошко с сообщением «Wrong serial».Code:.text:004011C6 mov ecx, 2 .text:004011CB cdq .text:004011CC mul ecx .text:004011CE xor dword_403210, eax .text:004011D4 jnz short loc_4011EA
Собственно, как происходит проверка я думаю мы поняли, теперь осталось рассмотреть функции модификации логина и частей серийного номера.
Начнем с модификации логина:
Видим, что в регистр есх помещается длина логина, а в регистр edi — сам логин. Далее esi помещаеться последний символ, затем esi складыдвается с eax ( который вначале был инициализирован нулем). И на последок к значению eax прибавляется значение ecx, которое уменьшается каждый раз на один тем самым обеспечивая, то что мы будем проделывать выше указанные действия со всеми символами введоно логина. Сам результат модифицирования логина после выполнения этого цикла будет записан в регистре eax. Для моего ника inisider ( это мой второй ник, кто еще не понял ;) ) он получается равен 37B.Code:.text:0040110C modifiengLogin proc near ; CODE XREF: sub_40112E+2B#p .text:0040110C .text:0040110C arg_0= dword ptr 4 .text:0040110C arg_4= dword ptr 8 .text:0040110C .text:0040110C push ecx .text:0040110D push edi .text:0040110E push esi .text:0040110F and eax, 0 .text:00401112 and esi, 0 .text:00401115 mov edi, [esp+0Ch+arg_0] .text:00401119 mov ecx, [esp+0Ch+arg_4] .text:0040111D .text:0040111D loc_40111D: ; CODE XREF: modifiengLogin+1A#j .text:0040111D movzx esi, byte ptr [ecx+edi-1] .text:00401122 add eax, esi .text:00401124 add eax, ecx .text:00401126 loop loc_40111D .text:00401128 pop esi .text:00401129 pop edi .text:0040112A pop ecx .text:0040112B retn 8 .text:0040112B modifiengLogin endp
Теперь рассмотрим функцию модифицирования частей введеного серийного номера:
Видим, что в edi помещается часть серийного номера ( их у нас 2 кто забыл, почему? Читаем выше, там все описано ;) ). Далее в esi заносится очередной символ введного серийного номера. Если он не равен 0 ( это именно тот 0, который отдельно вставлялся, что бы разбить введеный серийник на две части, каждый из которой модифицируется отдельно). Соотвественно, если очередной символ не 0, то отнимается от него 0х30 (result1) и сравниваем полученный результат с 0х0A. Если этот результат больше 0x0A, то мы дополнительно к нему (к result1) еще отнимаем 0х07 и сравниваем теперь с 0х17. Если он больше, то то что мы насчитали обнуляем; если нет, то просто запониманием, то что мы насчитали в result1. Далее мы сдвигаем итоговый результат на 4 и записываем на сдвинутые позиции result1. Повторяем все эти действия 8 раз.Code:.text:004010B8 modifiengSerial proc near ; CODE XREF: sub_40112E+53#p .text:004010B8 ; sub_40112E+63#p .text:004010B8 .text:004010B8 arg_0= dword ptr 4 .text:004010B8 .text:004010B8 push edi .text:004010B9 push esi .text:004010BA push ecx .text:004010BB and eax, 0 .text:004010BE and esi, 0 .text:004010C1 and ecx, 0 .text:004010C4 mov edi, [esp+0Ch+arg_0] .text:004010C8 .text:004010C8 loc_4010C8: ; CODE XREF: modifiengSerial+33#j .text:004010C8 movzx esi, byte ptr [ecx+edi] .text:004010CC test esi, esi .text:004010CE jz short loc_4010ED .text:004010D0 sub esi, 30h .text:004010D3 cmp esi, 0Ah .text:004010D6 jl short loc_4010E2 .text:004010D8 sub esi, 7 .text:004010DB cmp esi, 17h .text:004010DE jl short loc_4010E2 .text:004010E0 xor esi, esi .text:004010E2 .text:004010E2 loc_4010E2: ; CODE XREF: modifiengSerial+1E#j .text:004010E2 ; modifiengSerial+26#j .text:004010E2 shl eax, 4 .text:004010E5 or eax, esi .text:004010E7 inc ecx .text:004010E8 cmp ecx, 8 .text:004010EB jnz short loc_4010C8 .text:004010ED .text:004010ED loc_4010ED: ; CODE XREF: modifiengSerial+16#j .text:004010ED pop ecx .text:004010EE pop esi .text:004010EF pop edi .text:004010F0 retn 4 .text:004010F0 modifiengSerial endp
Собственно, всё. Мы рассмотрели всё, что касается генерации серийного номера и теперь можно описать роботу генератора ключей. ( к сожалению, код меня обламало писать, поэтому только опишу алгоритм и приведу пример для своего ника):
1. Вызываем функцию модицифрования логина.
2. Прибавляем к значению модифицированного логина значение 0xDDCCBBAA
3. Исходя из полученного значения мы начинаем генерировать первую часть нашего серейника.
4. Берем очередной символ результата.
5. Если он больше 0x7, то прибавляем к нему 7h, иначе прибавляем 0.
6. Если то, что мы получили на преидущем шаге больше 0x0А, то прибавляем к нему 0х30.
7. После выполнения шага 6, у нас есть оригинальный символ серийника.
8. Берем следующий символ результата.
9. Повторяем шаги 5 — 8 еще 7 раз.
10. Берем результат полученный на шаге 2 и умножаем его на 2.
11. Над полученным результатом выполняем шаги 4-9.
12. Получаем 2ю часть правильного серийника.
13. Ставим между ними любой символ.
14. Получаем сгенерированный серийный номер.
Теперь рассмотрим как это все работает на примере моего ника.
Функция модификации логина для моего ника выдала 0x37B.
Прибавляем 0xDDCCBBAA + 0x37B = DDCCBF25.
Берем первый символ D, он у нас больше 7h, поэтому прибавляем 7 получаем 14h. Т.к. 14H больше 0х0А, то прибавляем еще 14h, получаем 44h = D
Принцип думаю понятен поэтому я буду писать только расчеты:
Dh + 7h = 14h + 30h = 44h = D
Ch + 7h = 13h + 30h = 43h = C
Ch + 7h = 13h + 30h = 43h = C
Bh + 7h = 12h + 30h = 42h = B
Bh + 7h = 12h + 30h = 42h = B
Fh + 7h = 16h + 30h = 46h = F
2h + 0h = 2h + 30h = 32h = 2
5h + 7h = Ch + 30h = 3Ch = <
Таким образом мы получили первую часть серийного номера = DDCCBBF2<.
Для получения второй части нам надо DDCCBF25 * 2 = BB997E4A. И проделать тоже самое, что мы делали с DDCCBF25. Привожу только расчет:
Bh + 7h = 12h + 30h = 42h = B
Bh + 7h = 12h + 30h = 42h = B
9h + 7h = 10h + 30h = 40h = @
9h + 7h = 10h + 30h = 40h = @
7h + 7h = Eh + 30h = 3Eh = >
Eh + 7h = 15h + 30h = 45h = E
4h + 0h = 4h + 30h = 34h =4
Ah + 7h = 11h + 30h = 41h = A
Вторая часть получилась равной BB@@>E4A.
Теперь ставим между двумя частями любой символ, я выбрал 0, все равно он там потом будет ставится и получаем сгенерированый серийник для моего ника: DDCCBF2<0BB@@>E4A.
Теперь уже вообще всё. Всё, что хотел, то расскзаал.
Надеюсь это кому-то надо ;).
P.S.: Собственно навоял я генератор. Вот:
Code:#include <string.h> #include <stdio.h> #include <stdlib.h> #define SERIAL_LEN 0x11 char *login; // = "coldfire"; char serialNumber[SERIAL_LEN + 1]; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int modifyLogin() { int result = 0; int loginLen = strlen(login); char currChar; while (loginLen != 0) { currChar = *(login + loginLen - 1); result += currChar; result += loginLen; loginLen--; } return result; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void generateSerial(char *serial, int number) { int currNum = 0; int i = 7; for (; i != -1; i--) { currNum = (number & 0x0f); if (currNum >= 0x07) { currNum += 0x07; } currNum += 0x30; serial[i] = currNum; number >>= 4; } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void initSerialNumber() { int i = 0; for (; i < SERIAL_LEN; i++) { serialNumber[i] = 'a'; } serialNumber[SERIAL_LEN] = '\0'; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int main(int argc, char *argv[]) { if ((argc != 2)) { printf("USAGE: keygenme.exe LOGIN\n"); printf("EXAMPLE: keygenme.exe coldfire\n"); return 0; } login = argv[1]; if ((strlen(login) <= 5) && (strlen(login) % 2 != 0)) { printf("ERROR: length of login must be greater than 5 and be even\n"); return 0; } initSerialNumber(); int modifiedLogin = modifyLogin(); modifiedLogin += 0xDDCCBBAA; generateSerial(serialNumber, modifiedLogin); modifiedLogin *= 2; generateSerial(&serialNumber[9], modifiedLogin); printf("login:\t%s\nserial:\t%s\n", login, serialNumber); return 0; }



Reply With Quote
Thanks
