Исследование защиты программы IrfanView


Скриншот программы IrfanView

IrfanView - один из самых популярных просмотрщиков графики и в особом представлении не нуждается. IrfanView прекрасно работает без какой-либо регистрации, можно даже сказать, что он бесплатный. Регистрационный ключ высылается автором в качестве благодарности за добровольные пожертвования, в остальных случаях программа пишет, что зарегистрирована на "YOU". Отправку пожертвований я оставляю целиком на вашей совести, а сейчас мы просто разберем алгоритм регистрации программы.

Скачиваем дистрибутив, устанавливаем, смотрим что у нас получилось. Файл i_view32.exe упакован UPX, снимаем его им же самим, upx -d i_view32.exe, тут никаких сюрпризов. Дальше в работу традиционно подключается дизассемблер, мы же пока посмотрим на то, как программа регистрируется и как реагирует на ввод неправильных регистрационных данных.


Строка сообщения в ресурсах

Строка сообщения о неправильной регистрации находится в ресурсах файла. Ее индекс равен 1238 или 4D6h в шестнадцатеричной системе счисления. Поищем это значение в листинге дизассемблера:
.text:0047FBC1                 lea     edx, [esp+0B1Ch+Str]
.text:0047FBC8                 lea     eax, [esp+0B1Ch+Text]
.text:0047FBCF                 push    edx             ; Str
.text:0047FBD0                 push    eax             ; int
; Вызвать функцию проверки регистрационных данных
.text:0047FBD1                 call    sub_44B370
.text:0047FBD6                 add     esp, 8
; Если она вернула EAX=0, то регистрация неправильная
.text:0047FBD9                 test    eax, eax
.text:0047FBDB                 jnz     short loc_47FC04
; Загрузить строчку из ресурсов и вывести сообщение о неправильной регистрации
.text:0047FBDD                 mov     ecx, hInstance
.text:0047FBE3                 push    eax             ; int
.text:0047FBE4                 push    eax             ; int
.text:0047FBE5                 push    eax             ; int
.text:0047FBE6                 push    eax             ; int
; Индекс строки из ресурсов
.text:0047FBE7                 push    4D6h            ; uID
.text:0047FBEC                 push    ecx             ; hInstance
.text:0047FBED                 call    sub_46EBC0
.text:0047FBF2                 add     esp, 18h
.text:0047FBF5                 xor     eax, eax
.text:0047FBF7                 pop     edi
.text:0047FBF8                 pop     esi
.text:0047FBF9                 pop     ebp
.text:0047FBFA                 pop     ebx
.text:0047FBFB                 add     esp, 0B0Ch
.text:0047FC01                 retn    10h
.text:0047FC04 ; -----------------------------------------
.text:0047FC04 loc_47FC04:
; Сохранить регистрационные данные в ini-файле
.text:0047FC04                 mov     esi, ds:WritePrivateProfileStringA
.text:0047FC0A                 lea     edx, [esp+0B1Ch+Data]
.text:0047FC11                 lea     eax, [esp+0B1Ch+Text]
.text:0047FC18                 push    edx             ; lpFileName
.text:0047FC19                 push    eax             ; lpString
.text:0047FC1A                 push    offset aName    ; "Name"
.text:0047FC1F                 push    offset aRegistration ; "Registration"
.text:0047FC24                 call    esi ; WritePrivateProfileStringA
.text:0047FC26                 lea     ecx, [esp+0B1Ch+Data]
.text:0047FC2D                 lea     edx, [esp+0B1Ch+Str]
.text:0047FC34                 push    ecx             ; lpFileName
.text:0047FC35                 push    edx             ; lpString
.text:0047FC36                 push    offset aCode    ; "Code"
.text:0047FC3B                 push    offset aRegistration ; "Registration"
.text:0047FC40                 call    esi ; WritePrivateProfileStringA
.text:0047FC42                 push    0
.text:0047FC44                 push    0
.text:0047FC46                 push    0
.text:0047FC48                 push    0
.text:0047FC4A                 push    4D7h
.text:0047FC4F                 jmp     loc_481294

Код понятен. Сперва вызывается функция проверки, затем или выводится окно с сообщением о неправильной регистрации, или регистрационные данные записываются в ini-файл. Первый вариант решения, как обычно, патч функции проверки, чтобы она всегда возвращала EAX=1. Классический патч по адресу 0044B370 из двух команд MOV EAX,1 / RET и программа с радостью примет любой серийный номер.

Но так придется патчить каждую новую версию программы, что в конце концов надоест. Можно, конечно, сделать универсальный патч и даже inline-патч поверх упаковщика, но стоят ли такие усилия победы над бесплатной программой? Поэтому посмотрим, как проверяется регистрационный номер. Процедура очень простая, несмотря на множество арифметических и логических операций, весь код наглядный и сложности в анализе не представляет. Сперва высчитывается сумма кодов символов регистрационного имени, после чего значение дополнительно преобразуется несколькими операциями и после этого переводится в текстовый вид.
.text:0044B370                 mov     eax, [esp+Str]
.text:0044B374                 sub     esp, 14h
.text:0044B377                 push    ebx
.text:0044B378                 push    ebp
.text:0044B379                 push    esi
.text:0044B37A                 push    edi
.text:0044B37B                 push    eax             ; Str
.text:0044B37C                 xor     ebx, ebx
.text:0044B37E                 call    _atol
.text:0044B383                 mov     esi, [esp+28h+arg_0]
.text:0044B387                 mov     ebp, eax
.text:0044B389                 mov     edi, esi
.text:0044B38B                 or      ecx, 0FFFFFFFFh
.text:0044B38E                 xor     eax, eax
.text:0044B390                 add     esp, 4
.text:0044B393                 xor     edx, edx
.text:0044B395                 repne scasb
.text:0044B397                 not     ecx
.text:0044B399                 dec     ecx
.text:0044B39A                 test    ecx, ecx
.text:0044B39C                 jle     short loc_44B3B5
; Подсчитать сумму ASCII-кодов всех символов регистрационного имени
.text:0044B39E loc_44B39E:
.text:0044B39E                 movsx   ecx, byte ptr [edx+esi]
.text:0044B3A2                 add     ebx, ecx
.text:0044B3A4                 mov     edi, esi
.text:0044B3A6                 or      ecx, 0FFFFFFFFh
.text:0044B3A9                 xor     eax, eax
.text:0044B3AB                 inc     edx
.text:0044B3AC                 repne scasb
.text:0044B3AE                 not     ecx
.text:0044B3B0                 dec     ecx
.text:0044B3B1                 cmp     edx, ecx
.text:0044B3B3                 jl      short loc_44B39E
.text:0044B3B5 loc_44B3B5:
; Небольшие преобразования полученного значения
.text:0044B3B5                 mov     eax, 104h
.text:0044B3BA                 push    0Ah             ; Radix
.text:0044B3BC                 sub     eax, ebx
.text:0044B3BE                 cdq
.text:0044B3BF                 xor     eax, edx
.text:0044B3C1                 sub     eax, edx
.text:0044B3C3                 add     eax, 14Ch
.text:0044B3C8                 lea     edx, ds:0[eax*8]
.text:0044B3CF                 sub     edx, eax
.text:0044B3D1                 lea     ecx, [eax+edx*4]
.text:0044B3D4                 lea     edx, [esp+28h+Dest]
.text:0044B3D8                 push    edx             ; Dest
.text:0044B3D9                 lea     esi, [eax+ecx*2]
.text:0044B3DC                 shl     esi, 3
.text:0044B3DF                 push    esi             ; Value
; Перевести полученное значение в строку (десятичное значение)
.text:0044B3E0                 call    __ltoa
...

Дальше вычисления могут пойти по двум вариантам, в зависимости от полученного в предыдущем коде значения. Если оно больше 0F423Fh (или 999999 в десятичной системе счисления), то выполняется одна ветка алгоритма, если меньше или равно - другая. Зачем так сделано, будет понятно ниже. В нашем случае для регистрационного имени "ManHunter / PCL" предварительное значение будет "623040". Смотрим дальше.
; Значение больше 999999?
.text:0044B3E8                 cmp     esi, 0F423Fh
; Да, переход
.text:0044B3EE                 ja      loc_44B4E3
; Первая ветка алгоритма
.text:0044B3F4                 mov     cl, byte ptr [esp+24h+var_10]
.text:0044B3F8                 mov     al, byte ptr [esp+24h+var_10+1]
.text:0044B3FC                 mov     dl, [esp+24h+Dest+3]
.text:0044B400                 mov     byte ptr [esp+24h+var_10+2], cl
.text:0044B404                 mov     cl, [esp+24h+Dest+1]
.text:0044B408                 mov     [esp+18h], al
.text:0044B40C                 mov     al, [esp+24h+Dest+2]
.text:0044B410                 mov     byte ptr [esp+24h+var_10+1], dl
.text:0044B414                 mov     [esp+24h+Dest+2], cl
.text:0044B418                 mov     ecx, [esp+24h+var_10]
.text:0044B41C                 and     ecx, 0FFh
.text:0044B422                 mov     [esp+24h+Dest+3], al
.text:0044B426                 mov     eax, ecx
.text:0044B428                 shl     eax, 5
.text:0044B42B                 sub     eax, ecx
.text:0044B42D                 mov     ecx, [esp+18h]
.text:0044B431                 and     ecx, 0FFh
.text:0044B437                 lea     edx, [eax+eax*2]
.text:0044B43A                 lea     eax, [ecx+ecx*4]
.text:0044B43D                 shl     eax, 3
.text:0044B440                 sub     eax, ecx
.text:0044B442                 sub     eax, edx
.text:0044B444                 cdq
.text:0044B445                 mov     ecx, eax
.text:0044B447                 xor     ecx, edx
.text:0044B449                 sub     ecx, edx
.text:0044B44B                 lea     eax, [ecx+ecx*4]
.text:0044B44E                 shl     eax, 3
.text:0044B451                 sub     eax, ecx
.text:0044B453                 mov     ecx, 9
.text:0044B458                 cdq
.text:0044B459                 idiv    ecx
.text:0044B45B                 mov     eax, dword ptr [esp+24h+Dest+3]
.text:0044B45F                 and     eax, 0FFh
.text:0044B464                 add     dl, 30h
.text:0044B467                 mov     byte ptr [esp+24h+var_10+3], dl
.text:0044B46B                 lea     edx, [eax+eax*2]
.text:0044B46E                 shl     edx, 4
.text:0044B471                 sub     edx, eax
.text:0044B473                 mov     eax, [esp+24h+var_10+1]
.text:0044B477                 and     eax, 0FFh
.text:0044B47C                 lea     ecx, [eax+eax*8]
.text:0044B47F                 lea     eax, [eax+ecx*4]
.text:0044B482                 lea     eax, [edx+eax*2]
.text:0044B485                 cdq
.text:0044B486                 xor     eax, edx
.text:0044B488                 sub     eax, edx
.text:0044B48A                 lea     ecx, [eax+eax*8]
.text:0044B48D                 lea     eax, [eax+ecx*4]
.text:0044B490                 mov     ecx, 9
.text:0044B495                 shl     eax, 1
.text:0044B497                 cdq
.text:0044B498                 idiv    ecx
.text:0044B49A                 mov     ecx, dword ptr [esp+24h+Dest]
.text:0044B49E                 and     ecx, 0FFh
.text:0044B4A4                 lea     eax, [ecx+ecx*2]
.text:0044B4A7                 lea     eax, [eax+eax*8]
.text:0044B4AA                 shl     eax, 1
.text:0044B4AC                 sub     eax, ecx
.text:0044B4AE                 add     dl, 30h
.text:0044B4B1                 mov     byte ptr [esp+24h+var_10], dl
.text:0044B4B5                 mov     ecx, dword ptr [esp+24h+Dest+1]
.text:0044B4B9                 and     ecx, 0FFh
.text:0044B4BF                 lea     edx, ds:0[ecx*8]
.text:0044B4C6                 sub     edx, ecx
.text:0044B4C8                 lea     edx, [edx+edx*4]
.text:0044B4CB                 sub     eax, edx
.text:0044B4CD                 cdq
.text:0044B4CE                 mov     ecx, eax
.text:0044B4D0                 xor     ecx, edx
.text:0044B4D2                 sub     ecx, edx
.text:0044B4D4                 lea     eax, [ecx+ecx*2]
.text:0044B4D7                 lea     eax, [eax+eax*8]
.text:0044B4DA                 shl     eax, 1
.text:0044B4DC                 sub     eax, ecx
.text:0044B4DE                 jmp     loc_44B5D8
.text:0044B4E3 ; -----------------------------------------
; Вторая ветка алгоритма
.text:0044B4E3 loc_44B4E3:
.text:0044B4E3                 mov     al, byte ptr [esp+24h+var_10+1]
.text:0044B4E7                 mov     dl, byte ptr [esp+24h+var_10+2]
.text:0044B4EB                 mov     cl, byte ptr [esp+24h+var_10]
.text:0044B4EF                 mov     byte ptr [esp+24h+var_10+2], al
.text:0044B4F3                 mov     al, [esp+24h+Dest+1]
.text:0044B4F7                 mov     [esp+18h], dl
.text:0044B4FB                 mov     dl, [esp+24h+Dest+2]
.text:0044B4FF                 mov     [esp+24h+Dest+2], al
.text:0044B503                 mov     eax, [esp+24h+var_10+2]
.text:0044B507                 mov     byte ptr [esp+24h+var_10+1], cl
.text:0044B50B                 and     eax, 0FFh
.text:0044B510                 mov     [esp+24h+Dest+3], dl
.text:0044B514                 mov     ecx, eax
.text:0044B516                 shl     ecx, 6
.text:0044B519                 sub     ecx, eax
.text:0044B51B                 mov     eax, [esp+18h]
.text:0044B51F                 and     eax, 0FFh
.text:0044B524                 lea     eax, [eax+eax*8]
.text:0044B527                 shl     eax, 2
.text:0044B52A                 sub     eax, ecx
.text:0044B52C                 mov     ecx, 9
.text:0044B531                 cdq
.text:0044B532                 xor     eax, edx
.text:0044B534                 sub     eax, edx
.text:0044B536                 lea     eax, [eax+eax*8]
.text:0044B539                 shl     eax, 2
.text:0044B53C                 cdq
.text:0044B53D                 idiv    ecx
.text:0044B53F                 add     dl, 30h
.text:0044B542                 mov     byte ptr [esp+24h+var_10+3], dl
.text:0044B546                 mov     eax, [esp+24h+var_10]
.text:0044B54A                 and     eax, 0FFh
.text:0044B54F                 add     eax, 20h
.text:0044B552                 lea     edx, ds:0[eax*8]
.text:0044B559                 sub     edx, eax
.text:0044B55B                 lea     eax, [eax+edx*4]
.text:0044B55E                 lea     ecx, [eax+eax*2]
.text:0044B561                 mov     eax, dword ptr [esp+24h+Dest+3]
.text:0044B565                 and     eax, 0FFh
.text:0044B56A                 lea     edx, [eax+eax*4]
.text:0044B56D                 shl     edx, 3
.text:0044B570                 sub     edx, eax
.text:0044B572                 lea     eax, [ecx+edx*2]
.text:0044B575                 cdq
.text:0044B576                 xor     eax, edx
.text:0044B578                 sub     eax, edx
.text:0044B57A                 lea     ecx, ds:0[eax*8]
.text:0044B581                 sub     ecx, eax
.text:0044B583                 lea     eax, [eax+ecx*4]
.text:0044B586                 mov     ecx, 9
.text:0044B58B                 lea     eax, [eax+eax*2]
.text:0044B58E                 cdq
.text:0044B58F                 idiv    ecx
.text:0044B591                 mov     eax, dword ptr [esp+24h+Dest]
.text:0044B595                 and     eax, 0FFh
.text:0044B59A                 add     dl, 30h
.text:0044B59D                 mov     byte ptr [esp+24h+var_10], dl
.text:0044B5A1                 lea     edx, ds:0[eax*8]
.text:0044B5A8                 sub     edx, eax
.text:0044B5AA                 lea     eax, [eax+edx*4]
.text:0044B5AD                 mov     edx, dword ptr [esp+24h+Dest+1]
.text:0044B5B1                 and     edx, 0FFh
.text:0044B5B7                 mov     ecx, edx
.text:0044B5B9                 shl     ecx, 4
.text:0044B5BC                 add     ecx, edx
.text:0044B5BE                 shl     eax, 1
.text:0044B5C0                 lea     ecx, [ecx+ecx*4]
.text:0044B5C3                 sub     eax, ecx
.text:0044B5C5                 cdq
.text:0044B5C6                 xor     eax, edx
.text:0044B5C8                 sub     eax, edx
.text:0044B5CA                 lea     edx, ds:0[eax*8]
.text:0044B5D1                 sub     edx, eax
.text:0044B5D3                 lea     eax, [eax+edx*4]
.text:0044B5D6                 shl     eax, 1
.text:0044B5D8 loc_44B5D8:
.text:0044B5D8                 cdq
.text:0044B5D9                 mov     ecx, 9
.text:0044B5DE                 mov     [esp+24h+var_B], 0
.text:0044B5E3                 idiv    ecx
.text:0044B5E5                 add     dl, 30h
.text:0044B5E8                 mov     [esp+24h+Dest+1], dl
.text:0044B5EC                 lea     edx, [esp+24h+Dest]
; Преобразовать строку в десятичное число
.text:0044B5F0                 push    edx             ; Str
.text:0044B5F1                 call    _atol

На основании подсчитанного ранее значения выполняются его преобразования, а строка серийного номера дополняется несколькими символами. Проверка значения нужна для того, чтобы определить, в какие позиции записывать дополнительные символы, после 6-й или после 7-й цифры. Код обеих веток алгоритма линейный и настолько чистый, что его можно копировать в генератор ключей практически без изменений. В основном это двоичная арифметика. Небольшие нюансы есть при манипуляциях с символами, но они также становятся понятны при прохождении кода под отладчиком. Там же в отладчике по адресу 0044B5F0 регистр EDX указывает на правильный серийный номер для введенного имени. Можно на этом и остановиться, получив валидную пару, можно сделать сниффер ключей, а можно выдрать код из проверки и написать свой генератор.

Валидной парой, например, будет регистрационное имя "ManHunter / PCL" и серийный номер "642300400". Попробуем зарегистрировать с их помощью IrfanView. Все проходит на ура, программа принимает серийник и благодарит за регистрацию.


Программа успешно зарегистрирована

Таким образом решен еще один несложный ребус по реверс-инженерии. Мы даже умудрились не нанести никакого вреда индустрии программного обеспечения, так как программа бесплатная. А очередную порцию опыта, надеюсь, вы получили.
Теги:
защита, взлом
Добавлено: 16 Сентября 2013 11:57:19 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...