Tutorial for 010 Memorizer ^^^^^^^^^^^^^^^^^^^^^^^^^^ Интсрументы: PEiD, DeDe, Ollydebug, TableExtractor Уровень: 2 WWW : http://www.sweetscape.com/ Перевод: kaiZer/TEAM-53 Давайте начнем. Открываем файл с помощью PEiD и узнаем, что программа написана на Borland C++. Хмм, написана с помощью Borland. Может быть, мы должны попробовать DeDe. Делаем это и декомпилируем программу. Переходим в регистрационную форму и получаем адрес кнопки "OK". Адрес: 00411974. Теперь загружаем программу в Olly и запускаем программу. Устанавливаем брэйкпоинт на 00411974. До нажатия на кнопку "OK" надо ввести имя и серийный номер. Имя: ScareByte Серийный номер: 1928-3746-5564-7382 Мы можем видеть, что серийник состоит из 4 частей. Позже мы увидим, что серийник состоит из 8 частей ;) Каждые два числа - один байт. Итак, мы можем сказать, что серийник строится так: ab-cd-ef-gh, где a,b,c,d,e,f,g,h - все по одному байту. Теперь жмем кнопку "OK" и мы в Olly. Трейсим, пока не окажемся здесь: 00411B7F |. 6A 01 PUSH 1 00411B81 |. FF35 14F54900 PUSH DWORD PTR DS:[49F514] 00411B87 |. E8 D8160000 CALL 010Memor.00413264 00411B8C |. 83C4 08 ADD ESP,8 00411B8F |. 8945 98 MOV DWORD PTR SS:[EBP-68],EAX 00411B92 |. 6A 01 PUSH 1 ; /Arg2 = 00000001 00411B94 |. FF35 14F54900 PUSH DWORD PTR DS:[49F514] ; |Arg1 = 010BC54C 00411B9A |. E8 55160000 CALL 010Memor.004131F4 ; \010Memor.004131F4 00411B9F |. 83C4 08 ADD ESP,8 00411BA2 |. 8945 94 MOV DWORD PTR SS:[EBP-6C],EAX 00411BA5 |. 817D 94 FC0100>CMP DWORD PTR SS:[EBP-6C],1FC 00411BAC |. 75 35 JNZ SHORT 010Memor.00411BE3 00411BAE |. 66:C745 B4 500>MOV WORD PTR SS:[EBP-4C],50 00411BB4 |. BA B4494A00 MOV EDX,010Memor.004A49B4 ; ASCII "Password accepted. Thanks for registering!" Переходим в Call по адресу 00411B87 и мы видим, что происходит с нашим именем и серийником... Я буду использовать части серийника (ab-cd-ef-gh) для лучшей работы. 0041329E . 83C4 08 ADD ESP,8 004132A1 . 807D F3 5A CMP BYTE PTR SS:[EBP-D],5A // d = 5A ? 004132A5 . 75 79 JNZ SHORT 010Memor.00413320 // если нет => прыжок => не зарегистрирована Здесь мы видим, что наш серийник имеет константу. Если она не равна, то прыгаем и сравниваем с 6A. Но это все равно неправильно. Я проверил ;) Итак, идем дальше... 004132A7 . 8A4D F0 MOV CL,BYTE PTR SS:[EBP-10] // CL = a (19) 004132AA . 324D F4 XOR CL,BYTE PTR SS:[EBP-C] // CL = a xor e = 19 xor 55 = 4C 004132AD . 884D EF MOV BYTE PTR SS:[EBP-11],CL // EBP-11 = r = a xor e = 4C В EBP-11 помещается результат таких действий: r = a xor e 004132B0 . 8A45 F1 MOV AL,BYTE PTR SS:[EBP-F] // AL = b (28) 004132B3 . 3245 F5 XOR AL,BYTE PTR SS:[EBP-B] // AL = b xor f = 28 xor 64 = 4C 004132B6 . 33D2 XOR EDX,EDX // очищаем EDX 004132B8 . 8AD0 MOV DL,AL // EDX = 0000004C 004132BA . C1E2 08 SHL EDX,8 // EDX shl 8 = 00004C00 004132BD . 8A4D F2 MOV CL,BYTE PTR SS:[EBP-E] // CL = c (37) 004132C0 . 324D F6 XOR CL,BYTE PTR SS:[EBP-A] // CL = c xor g = 37 xor 73 = 44 004132C3 . 33C0 XOR EAX,EAX // очишаем EAX 004132C5 . 8AC1 MOV AL,CL // AL = CL = 44 004132C7 . 66:03D0 ADD DX,AX // DX = DX + AX = 4C44 004132CA . 66:8955 EC MOV WORD PTR SS:[EBP-14],DX // EBP-14 = p = (b xor f) shl 8 + c xor g = 4C44 В EBP-14 помещается результат таких действий: p = (b xor f) shl 8 + c xor g 004132CE . 8A55 EF MOV DL,BYTE PTR SS:[EBP-11] // DL = r 004132D1 . 52 PUSH EDX ; /Arg1 = r 004132D2 . E8 75170000 CALL 010Memor.00414A4C ; \010Memor.00414A4C 00414A4C /$ 55 PUSH EBP // 00414A4D |. 8BEC MOV EBP,ESP // 00414A4F |. 51 PUSH ECX // 00414A50 |. 8A45 08 MOV AL,BYTE PTR SS:[EBP+8] // AL = Arg1 00414A53 |. 34 A7 XOR AL,0A7 // AL = Arg1 xor A7 00414A55 |. 04 C1 ADD AL,0C1 // AL = (Arg1 xor A7) + C1 00414A57 |. 34 C9 XOR AL,0C9 // AL = ((Arg1 xor A7) + C1) xor C9 00414A59 |. 8845 FF MOV BYTE PTR SS:[EBP-1],AL 00414A5C |. 8A45 FF MOV AL,BYTE PTR SS:[EBP-1] 00414A5F |. 59 POP ECX 00414A60 |. 5D POP EBP 00414A61 \. C3 RETN Это маленькая функция. Результат = ((Arg1 xor A7) + C1) xor C9 => После этого CALL EAX = 65 004132D7 . 59 POP ECX 004132D8 . 33C9 XOR ECX,ECX 004132DA . 8AC8 MOV CL,AL // CL = результат из CALL = 65 004132DC . 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8] // 004132DF . 8948 18 MOV DWORD PTR DS:[EAX+18],ECX // q = результат из CALL = 65 Здесь производятся такие действия: q = ((Arg1 xor A7) + C1) xor C9 004132E2 . 66:8B55 EC MOV DX,WORD PTR SS:[EBP-14] // DX = p 004132E6 . 52 PUSH EDX ; /Arg1 = p 004132E7 . E8 76170000 CALL 010Memor.00414A62 ; \010Memor.00414A62 00414A62 /$ 55 PUSH EBP 00414A63 |. 8BEC MOV EBP,ESP 00414A65 |. 51 PUSH ECX 00414A66 |. 66:8B45 08 MOV AX,WORD PTR SS:[EBP+8] // AX = Arg1 00414A6A |. 66:35 928E XOR AX,8E92 // AX = Arg1 xor 8E92 00414A6E |. 66:05 FAB3 ADD AX,0B3FA // AX = (Arg1 xor 8E92) + B3FA 00414A72 |. 66:35 3175 XOR AX,7531 // AX = ((Arg1 xor 8E92) + B3FA) xor 7531 00414A76 |. 66:8945 FE MOV WORD PTR SS:[EBP-2],AX // сохраняем AX в EBP-2 00414A7A |. 0FB745 FE MOVZX EAX,WORD PTR SS:[EBP-2] // 00414A7E |. B9 0D000000 MOV ECX,0D // 00414A83 |. 99 CDQ // 00414A84 |. F7F9 IDIV ECX // EAX = EBP-2 div D и EDX = EBP-2 mod D 00414A86 |. 85D2 TEST EDX,EDX // если EDX <> 0 00414A88 |. 75 0F JNZ SHORT 010Memor.00414A99 // то прыгаем => результат = 0 00414A8A |. 0FB745 FE MOVZX EAX,WORD PTR SS:[EBP-2] // иначе результат = EBP-2 div D 00414A8E |. B9 0D000000 MOV ECX,0D // 00414A93 |. 99 CDQ // 00414A94 |. F7F9 IDIV ECX // 00414A96 |. 59 POP ECX // 00414A97 |. 5D POP EBP // 00414A98 |. C3 RETN // 00414A99 |> 33C0 XOR EAX,EAX // 00414A9B |. 59 POP ECX // 00414A9C |. 5D POP EBP // 00414A9D \. C3 RETN // Легко понять. У нас есть аргумент Arg1, который проксорен и сложен с некоторыми переменными. Если результат, который сохранен в EBP-2, равен mod D = 0, то результат = EBP-2 div D. Иначе результат равен 0. EBP-2 = ((Arg1 xor 8E92) + B3FA) xor 7531 EBP-2 mod D 0 : результат = EBP-2 div D иначе: результат = 0 В нашем примере у нас есть: EBP-2 = ((4C44 xor 8E92) + B3FA) xor 7531 = 03E1 EBP-2 mod D = 5 Итак, наш результат в ЕАХ = 0 004132EC . 59 POP ECX 004132ED . 0FB7C8 MOVZX ECX,AX // ECX = результат из CALL = 0 004132F0 . 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8] // 004132F3 . 8948 1C MOV DWORD PTR DS:[EAX+1C],ECX // s = результат из CALL = 0 Здесь проиводятся такие действия: s = (((Arg1 xor 8E92) + B3FA) xor 7531) div D если mod D = 0, иначе s = 0 004132F6 . 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8] // 004132F9 . 837A 18 00 CMP DWORD PTR DS:[EDX+18],0 // если q = 0 (изменения не нужны, потому что q = 65) 004132FD .-- 74 15 JE SHORT 010Memor.00413314 // то прыжок => не зарегистрирована 004132FF | . 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8] // 00413302 | . 8379 1C 00 CMP DWORD PTR DS:[ECX+1C],0 // если s = 0 (заменяем s на 2) 00413306 |-- 74 0C JE SHORT 010Memor.00413314 // то прыжок => не зарегистрирована 00413308 | . 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8] // 0041330B | . 8178 1C E80300>CMP DWORD PTR DS:[EAX+1C],3E8 // если s > 3E8 00413312 | . 76 32 JBE SHORT 010Memor.00413346 // то не прыгаем => не зарегистрирована 00413314 '-> B8 2A000000 MOV EAX,2A // не зарегистрирована 00413319 . E9 24010000 JMP 010Memor.00413442 // Покидаем CALL регистрации с результатом = 2A Итак, у нас что-то есть для составления серийного номера. Изменяем переменные. Мы должны перейти в процедуру регистрации... ........ 00413346 > 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8] // 00413349 . FF72 1C PUSH DWORD PTR DS:[EDX+1C] // s 0041334C . 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8] // 0041334F . FF71 18 PUSH DWORD PTR DS:[ECX+18] // q 00413352 . 807D F3 5A CMP BYTE PTR SS:[EBP-D],5A // если d = 5A (должно быть 5A) 00413356 . 0F94C0 SETE AL // AL должен быть 1 (потому что d = 5A) 00413359 . 83E0 01 AND EAX,1 // 0041335C . 50 PUSH EAX // EAX (1) 0041335D . 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8] // 00413360 . 83C0 04 ADD EAX,4 // сейчас EAX содержит наше имя 00413363 . E8 D4F3FEFF CALL 010Memor.0040273C // длина имени должна быть > 0 00413368 . 50 PUSH EAX ; |Arg1 = имя 00413369 . E8 46140000 CALL 010Memor.004147B4 ; \010Memor.004147B4 004147B4 /$ 55 PUSH EBP 004147B5 |. 8BEC MOV EBP,ESP 004147B7 |. 83C4 F0 ADD ESP,-10 004147BA |. 53 PUSH EBX 004147BB |. 33C0 XOR EAX,EAX 004147BD |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX // EBP-4 = 0 004147C0 |. FF75 08 PUSH DWORD PTR SS:[EBP+8] // 004147C3 |. E8 7CB00700 CALL 010Memor.0048F844 // EAX = длина имени 004147C8 |. 59 POP ECX 004147C9 |. 8945 F0 MOV DWORD PTR SS:[EBP-10],EAX // EBP-10 = длина имени 004147CC |. 33D2 XOR EDX,EDX 004147CE |. 8955 F4 MOV DWORD PTR SS:[EBP-C],EDX // EBP-C = 0 <= наш счетчик 004147D1 |. 8B4D F4 MOV ECX,DWORD PTR SS:[EBP-C] 004147D4 |. 3B4D F0 CMP ECX,DWORD PTR SS:[EBP-10] // если длина имени = 0 004147D7 |. 0F8D 34010000 JGE 010Memor.00414911 // то прыжок 004147DD |> 8B45 08 /MOV EAX,DWORD PTR SS:[EBP+8] // EAX содержит имя 004147E0 |. 8B55 F4 |MOV EDX,DWORD PTR SS:[EBP-C] // EDX = счетчик 004147E3 |. 0FBE0C10 |MOVSX ECX,BYTE PTR DS:[EAX+EDX] // ECX = символ из имени 004147E7 |. 894D F8 |MOV DWORD PTR SS:[EBP-8],ECX // сохраняем его в EBP-8 004147EA |. 837D 0C 00 |CMP DWORD PTR SS:[EBP+C],0 // вы помните? 00413356 SETE AL (AL должен быть 1) 004147EE |. 0F84 86000000 |JE 010Memor.0041487A // если нет, то прыжок 004147F4 |. 8B45 F8 |MOV EAX,DWORD PTR SS:[EBP-8] // ..... 004147F7 |. 8B1485 8C634A0>|MOV EDX,DWORD PTR DS:[EAX*4+4A638C] // несколько операций... уух ;) 004147FE |. 0355 FC |ADD EDX,DWORD PTR SS:[EBP-4] 00414801 |. 8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] 00414804 |. 83C1 0D |ADD ECX,0D 00414807 |. 81E1 FF000000 |AND ECX,0FF 0041480D |. 33148D 8C634A0>|XOR EDX,DWORD PTR DS:[ECX*4+4A638C] 00414814 |. 8B45 F8 |MOV EAX,DWORD PTR SS:[EBP-8] 00414817 |. 83C0 2F |ADD EAX,2F 0041481A |. 25 FF000000 |AND EAX,0FF 0041481F |. 0FAF1485 8C634>|IMUL EDX,DWORD PTR DS:[EAX*4+4A638C] 00414827 |. 6B4D F4 13 |IMUL ECX,DWORD PTR SS:[EBP-C],13 0041482B |. 81E1 FF000000 |AND ECX,0FF 00414831 |. 03148D 8C634A0>|ADD EDX,DWORD PTR DS:[ECX*4+4A638C] 00414838 |. 8B45 F4 |MOV EAX,DWORD PTR SS:[EBP-C] 0041483B |. 8D04C0 |LEA EAX,DWORD PTR DS:[EAX+EAX*8] 0041483E |. 8B4D 10 |MOV ECX,DWORD PTR SS:[EBP+10] 00414841 |. 8BD9 |MOV EBX,ECX 00414843 |. C1E1 04 |SHL ECX,4 00414846 |. 03CB |ADD ECX,EBX 00414848 |. 03C1 |ADD EAX,ECX 0041484A |. 25 FF000000 |AND EAX,0FF 0041484F |. 031485 8C634A0>|ADD EDX,DWORD PTR DS:[EAX*4+4A638C] 00414856 |. 6B45 F4 0D |IMUL EAX,DWORD PTR SS:[EBP-C],0D 0041485A |. 8B4D 14 |MOV ECX,DWORD PTR SS:[EBP+14] 0041485D |. 8BD9 |MOV EBX,ECX 0041485F |. C1E1 04 |SHL ECX,4 00414862 |. 2BCB |SUB ECX,EBX 00414864 |. 03C1 |ADD EAX,ECX 00414866 |. 25 FF000000 |AND EAX,0FF 0041486B |. 031485 8C634A0>|ADD EDX,DWORD PTR DS:[EAX*4+4A638C] 00414872 |. 8955 FC |MOV DWORD PTR SS:[EBP-4],EDX 00414875 |. E9 88000000 |JMP 010Memor.00414902 // прыжок в конец 0041487A |> 8B45 F8 |MOV EAX,DWORD PTR SS:[EBP-8] // начинаем ложную процедуру проверки, если AL был 0 0041487D |. 8B1485 8C634A0>|MOV EDX,DWORD PTR DS:[EAX*4+4A638C] // пропускаем это.. это бесполезно 00414884 |. 0355 FC |ADD EDX,DWORD PTR SS:[EBP-4] // если бы вы хотели использовать эту ложную процедуру, используйте ее! 00414887 |. 8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] // получаем после регистрации другое сообщение: 0041488A |. 83C1 3F |ADD ECX,3F // Trial Period is expired. do we want that? нет! 0041488D |. 81E1 FF000000 |AND ECX,0FF // ...... 00414893 |. 33148D 8C634A0>|XOR EDX,DWORD PTR DS:[ECX*4+4A638C] // я вырезал это. это тоже бесполезно.... ........ 00414902 |> FF45 F4 |INC DWORD PTR SS:[EBP-C] // увеличиваем счетчик 00414905 |. 8B45 F4 |MOV EAX,DWORD PTR SS:[EBP-C] // EAX = счетчик 00414908 |. 3B45 F0 |CMP EAX,DWORD PTR SS:[EBP-10] // если EAX < длины имени 0041490B |.^0F8C CCFEFFFF \JL 010Memor.004147DD // то берем след. символ 00414911 |> 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4] // результат в EAX 00414914 |. 5B POP EBX 00414915 |. 8BE5 MOV ESP,EBP 00414917 |. 5D POP EBP 00414918 \. C3 RETN Мы видим, что это "маленькая" самопальная функция, считающая контрольную 32-битную сумму. Она использует таблицу по адресу 4A638C. Мы можем скопировать ее. Эта таблица длиной 256, которая содержит данные типа DWORD. Мы можем перекодировать или рипнуть эту функцию. Я перекодировал ее на Delphi. Это не тяжело. Помните, что она использует больше, чем 1 параметр. Это CALL: function 010Memor.004147B4 (name: string; check: byte; q, s: dword): dword; Переменная check должна быть всегда 1. Итак, мы должны перекодировать первую часть этой конструкции. Если check = 0, то мы попадаем в ложную процедуру. Результат использования: function 010Memor.004147B4 ('ScareByte', 1, 65, 2) = 8A9445A0 0041336E . 83C4 10 ADD ESP,10 // 00413371 . 8945 FC MOV DWORD PTR SS:[EBP-4],EAX // EBP-4 = результат из контрольной суммы = 8A9445A0 00413374 . 8A55 FC MOV DL,BYTE PTR SS:[EBP-4] // 00413377 . 80E2 FF AND DL,0FF // DL = EBP-4 and FF = A0 0041337A . 3A55 F4 CMP DL,BYTE PTR SS:[EBP-C] // if e = DL (d из нашего серийника) 0041337D . 74 0A JE SHORT 010Memor.00413389 // then прыжок 0041337F . B8 2A000000 MOV EAX,2A // else незарегистрирована 00413384 . E9 B9000000 JMP 010Memor.00413442 // покидаем регистрацию 00413389 > 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4] // 0041338C . C1EA 08 SHR EDX,8 // 0041338F . 80E2 FF AND DL,0FF // DL = (EBP-4 shr 8) and FF = 45 00413392 . 3A55 F5 CMP DL,BYTE PTR SS:[EBP-B] // if f = DL 00413395 . 74 0A JE SHORT 010Memor.004133A1 // then прыжок 00413397 . B8 2A000000 MOV EAX,2A // else незарегистирована 0041339C . E9 A1000000 JMP 010Memor.00413442 // покидаем регистрацию 004133A1 > 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4] // 004133A4 . C1EA 10 SHR EDX,10 // 004133A7 . 80E2 FF AND DL,0FF // DL = (EBP-4 shr 10) and FF = 94 004133AA . 3A55 F6 CMP DL,BYTE PTR SS:[EBP-A] // if g = DL 004133AD . 74 0A JE SHORT 010Memor.004133B9 // then прыжок 004133AF . B8 2A000000 MOV EAX,2A // else незарегистирована 004133B4 . E9 89000000 JMP 010Memor.00413442 // покидаем регистрацию 004133B9 > 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4] // 004133BC . C1EA 18 SHR EDX,18 // 004133BF . 80E2 FF AND DL,0FF // DL = (EBP-4 shr 18) and FF = 8A 004133C2 . 3A55 F7 CMP DL,BYTE PTR SS:[EBP-9] // if h = DL 004133C5 . 74 07 JE SHORT 010Memor.004133CE // then прыжок 004133C7 . B8 2A000000 MOV EAX,2A // else незарегистирована 004133CC . EB 74 JMP SHORT 010Memor.00413442 // покидаем регистрацию 004133CE > 807D F3 5A CMP BYTE PTR SS:[EBP-D],5A // if d <> 5A 004133D2 . 75 19 JNZ SHORT 010Memor.004133ED // then прыжок 004133D4 . 8B55 0C MOV EDX,DWORD PTR SS:[EBP+C] // EDX = EBP+C = 1 (remember: 00411B7F PUSH 1) 004133D7 . 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8] // 004133DA . 3B51 18 CMP EDX,DWORD PTR DS:[ECX+18] // if q > 1 004133DD . 76 07 JBE SHORT 010Memor.004133E6 // then зарегистрирована :D 004133DF . B8 80000000 MOV EAX,80 // else зарегистрирована :D 004133E4 . EB 5C JMP SHORT 010Memor.00413442 004133E6 > B8 22000000 MOV EAX,22 004133EB . EB 55 JMP SHORT 010Memor.00413442 Итак, это все, что нам нужно, чтобы сделать кейген. Мы зарегистрированы в обоих случаях. если q > 1 или q = 1. Здесь мы могли бы сделать водяной знак. Но мы увидим. Первое, что мы увидим, это то, что получилось. Наш серийник строится так: ab-cd-ef-gh У нас есть несколько переменных: r: byte; p: word; q, s, checksum: dword; r = a xor e q = ((r xor A7) + C1) xor C9 p = (b xor f) shl 8 + c xor g s = (((Arg1 xor 8E92) + B3FA) xor 7531) div D if modulo D = 0 else s = 0 checksum = function 010Memor.004147B4 (name, 1, q, s) Вот все данные: 1.) d = 5A 2.) q > 0 3.) 1 <= s <= 3E8 4.) e = checksum and FF 5.) f = (checksum shr 8) and FF 6.) g = (checksum shr 10) and FF 7.) h = (checksum shr 18) and FF Интересная процедура регистрации. В конце мы должны пореверсить несколько функций. Но это очень легко ;) Решение: 1.) d = 5A - Мы не должны тут ничего делать ;) 2.) q > 0 r = a xor e Мы видим, что e используется в этой процедуре. Мы должны заменить эту функцию на a, потому что e - фиксированное число из контрольной суммы. q = ((r xor A7) + C1) xor C9 q = (((a xor e) xor A7) + C1) xor C9 | xor C9 q xor C9 = ((a xor e) xor A7) + C1 | - C1 (q xor C9) - C1 = (a xor e) xor A7 | xor A7 ((q xor C9) - C1) xor A7 = a xor e | xor e (((q xor C9) - C1) xor A7) xor e = a => a = (((q xor C9) - C1) xor A7) xor e '-> может быть изменено .. Водяные знаки .. Но должно быть <> 0. 3.) 1 <= s <= 3E8 (Позже мы увидим, что s - число лицензий .. От 1 до 1000 лицензий) p = (b xor f) shl 8 + c xor g f и g - фиксированные числа из контрольной суммы. Мы должны заменить их на b и c. s = (((p xor 8E92) + B3FA) xor 7531) div D if modulo D = 0 else s = 0 s = (((p xor 8E92) + B3FA) xor 7531) div D | * D s * D = ((p xor 8E92) + B3FA) xor 7531 | xor 7531 (s * D) xor 7531 = (p xor 8E92) + B3FA | - B3FA ((s * D) xor 7531) - B3FA = p xor 8E92 | xor 8E92 (((s * D) xor 7531) - B3FA) xor 8E92 = p => p = (((s * D) xor 7531) - B3FA) xor 8E92 => c = (p and FF) xor g => b = ((p shr 8) and FF) xor f 4. - 7.) из контрольной суммы. Ничего интересного. Исходники кейгена на Делфи. function chksum(user: string; q, s: dword): dword; var i: word; c: byte; const _004A638C : array[0..255] of dword = (....); // Я не вставлял полную таблицу, всатвьте ее сами ;) begin result := 0; for i := 0 to length(user)-1 do begin c := byte(user[i+1]); result := ((_004A638C[c] + result) xor _004A638C[(c + $0D) and $FF]) * _004A638C[(c + $2F) and $FF] + _004A638C[(i * $13) and $FF] + _004A638C[(i * $09 + $11 * q) and $FF] + _004A638C[(i * $0D + $0F * s) and $FF] end; end; // число лицензий (lnr) должно быть между 1 и 1000. Проверяйте это в своем кейгене. function generate(user: string; lnr: word): string; var a, b, c, e, f, g, h: byte; p : word; q, s, checksum : dword; begin // Получаем водяной знак .. w > 0 // Водяной знак зависит от имени пользователя. // $754346 - для uCF q := 0; for a := 1 to length(user) do q := q xor byte(user[a]); q := ((q * $754346) mod $FF) + 1; // Получаем s из числа лицензий s := lnr; // Получаем контрольную сумму checksum := chksum(user, q, s); // Считаем части серийника e := checksum and $FF; f := (checksum shr $08) and $FF; g := (checksum shr $10) and $FF; h := (checksum shr $18) and $FF; p := (((s * $0D) xor $7531) - $B3FA) xor $8E92; a := (((q xor $C9) - $C1) xor $A7) xor e; b := ((p shr 8) and $FF) xor f; c := (p and $ff) xor g; result := inttohex(a shl 8 or b, 4) + '-' + inttohex(c shl 8 or $5A, 4) + '-' + inttohex(e shl 8 or f, 4) + '-' + inttohex(g shl 8 or h, 4); end; greets to all ucf members (i love you all :) done by scarebyte [uCF] _______________________________________________ mail: scarebyte@gmx.net / scarebyte@youceff.com www: http://scared.at/scarebyte