Licencjonowanie i zabezpieczenie oprogramowania

Upubliczniłem na GitHubie repozytorium z SDK dla swojego systemu ochrony i licencjonowania oprogramowania PELock v2.0.

Zaawansowane ustawienia ochrony aplikacji w PELock v2.0

SDK i setki przykładów użycia są dostępne w wersji dla C/C++, D, Delphi/Pascal, PureBasic, PowerBASIC oraz MASM.

Podgląd okienka z identyfikatorem sprzętowym - blokada na sprzętowy identyfikator w PELock v2.0

Jeśli interesujesz się metodami ochrony oprogramowania przed złamaniem być może znajdziesz także coś dla siebie, ponieważ PELock v2.0 oferuje wiele nowych, niespotykanych metod pozwalających zabezpieczyć oprogramowanie przed crackingiem i reverse engineeringiem.

https://github.com/PELock/PELock-Software-Protection-and-Licensing-SDK

Technologiczna bieda kompilatora PureBasic

Nawiązując do artykułu o kompilatorze Go, nie mogę ominąć kompilatora PureBasic w wersji od 4.xx do 5.30. Jest to kompilator dla kolejnej odmiany języka BASIC, której developerzy są głusi na wszelkie maile dotyczące nieprawidłowości w kodzie i w ogóle „nie rozumieją o co chodzi”.

Więc o co tym razem mi chodzi? Analizowałem plik kompilatora PureBasic.exe w wersji 5.30. Może zacznijmy od optymalizacji.

1. Optymalizacja

Optymalizacja to słowo, które jest obce twórcom kompilatora PureBasic. W poprzednim artykule ktoś sugerował, że biedna optymalizacja kompilatora Go być może ma jakiś „sens”, na pewno jakiś gimbus, który nie rozróżnia assemblera od akumulatora, no więc być może i tutaj znajdziesz jakiś sens (czekam na komentarz):

.code:004F165E                 mov     ebp, [esp+10h+arg_70]
.code:004F1665                 push    ebp
.code:004F1666                 pop     ebp
.code:004F1667                 mov     ebx, [ebp+684h]
.code:004F166D                 cmp     ebx, [esp+10h+var_10]
.code:004F1670                 jnz     short loc_4F16A9
.code:004F1672                 push    0
.code:004F1677                 mov     ebp, [esp+14h+arg_70]
.code:004F167E                 push    ebp
.code:004F167F                 pop     ebp
.code:004F1680                 push    dword ptr [ebp+688h]
.code:004F1686                 call    sub_5743CC

Nie wiem jak można by i to zaklasyfikować? push ebp + pop ebp = zero efektu, wyrównanie kolejnych instrukcji też nie.

Kolejny genialny fragment kodu:

.code:004F1FBE sub_4F1FBE      proc near               ; DATA XREF: start+50C7o
.code:004F1FBE                 push    dword_703918
.code:004F1FC4                 pop     eax
.code:004F1FC5                 mov     dword_70411C, eax
.code:004F1FCA                 push    dword_7039CC
.code:004F1FD0                 pop     eax
.code:004F1FD1                 mov     dword_703EE4, eax
.code:004F1FD6                 push    dword_703F08
.code:004F1FDC                 pop     eax
.code:004F1FDD                 mov     dword_703CC0, eax
.code:004F1FE2                 xor     eax, eax
.code:004F1FE4                 retn    4
.code:004F1FE4 sub_4F1FBE      endp

Widać ktoś nie doczytał, że instrukcją mov można również odczytać dane z pamięci, pomijając dodatkowo stos.

Teraz trochę dłuższy fragment, w którym być może zauważycie pewien powtarzający się wzorzec:

.code:00401B0E                 mov     ebp, dword_705314
.code:00401B14                 mov     dword ptr [ebp+0Ch], 31h
.code:00401B1B                 mov     dword ptr [ebp+10h], 5Bh
.code:00401B22                 add     ebp, 8
.code:00401B25                 push    dword ptr [ebp+0Dh]
.code:00401B28                 call    sub_56E513
.code:00401B2D                 mov     ebp, dword_705314
.code:00401B33                 add     ebp, 8
.code:00401B36                 mov     ebp, [ebp+11h]
.code:00401B39                 mov     edx, offset off_696024 ; Str
.code:00401B3E                 lea     ecx, [ebp+8]    ; int
.code:00401B41                 call    sub_540558
.code:00401B46                 mov     ebp, dword_705314
.code:00401B4C                 add     ebp, 8
.code:00401B4F                 mov     ebp, [ebp+11h]
.code:00401B52                 mov     dword ptr [ebp+0Ch], 0C80000h
.code:00401B59                 mov     ebp, dword_705314
.code:00401B5F                 add     ebp, 8
.code:00401B62                 mov     ebp, [ebp+11h]
.code:00401B65                 mov     dword ptr [ebp+10h], 1
.code:00401B6C                 mov     ebp, dword_705314
.code:00401B72                 add     ebp, 8
.code:00401B75                 push    dword ptr [ebp+0Dh]
.code:00401B78                 call    sub_56E513
.code:00401B7D                 mov     ebp, dword_705314
.code:00401B83                 add     ebp, 8
.code:00401B86                 mov     ebp, [ebp+11h]
.code:00401B89                 mov     edx, offset off_6953F2 ; Str
.code:00401B8E                 lea     ecx, [ebp+8]    ; int
.code:00401B91                 call    sub_540558
.code:00401B96                 mov     ebp, dword_705314
.code:00401B9C                 add     ebp, 8
.code:00401B9F                 mov     ebp, [ebp+11h]
.code:00401BA2                 mov     dword ptr [ebp+0Ch], 0CA0000h
.code:00401BA9                 mov     ebp, dword_705314
.code:00401BAF                 add     ebp, 8
.code:00401BB2                 mov     ebp, [ebp+11h]
.code:00401BB5                 mov     dword ptr [ebp+10h], 2
.code:00401BBC                 mov     ebp, dword_705314
.code:00401BC2                 add     ebp, 8
.code:00401BC5                 push    dword ptr [ebp+0Dh]
.code:00401BC8                 call    sub_56E513
.code:00401BCD                 mov     ebp, dword_705314
.code:00401BD3                 add     ebp, 8
.code:00401BD6                 mov     ebp, [ebp+11h]
.code:00401BD9                 mov     edx, offset off_691FE6 ; Str
.code:00401BDE                 lea     ecx, [ebp+8]    ; int
.code:00401BE1                 call    sub_540558
.code:00401BE6                 mov     ebp, dword_705314
.code:00401BEC                 add     ebp, 8
.code:00401BEF                 mov     ebp, [ebp+11h]
.code:00401BF2                 mov     dword ptr [ebp+0Ch], 0C90000h
.code:00401BF9                 mov     ebp, dword_705314
.code:00401BFF                 add     ebp, 8
.code:00401C02                 mov     ebp, [ebp+11h]
.code:00401C05                 mov     dword ptr [ebp+10h], 4
.code:00401C0C                 mov     ebp, dword_705314
.code:00401C12                 add     ebp, 8
.code:00401C15                 push    dword ptr [ebp+0Dh]
.code:00401C18                 call    sub_56E513
.code:00401C1D                 mov     ebp, dword_705314
.code:00401C23                 add     ebp, 8
.code:00401C26                 mov     ebp, [ebp+11h]
.code:00401C29                 mov     edx, offset off_691198 ; Str
.code:00401C2E                 lea     ecx, [ebp+8]    ; int
.code:00401C31                 call    sub_540558
.code:00401C36                 mov     ebp, dword_705314
.code:00401C3C                 add     ebp, 8
.code:00401C3F                 mov     ebp, [ebp+11h]
.code:00401C42                 mov     dword ptr [ebp+0Ch], 0C40000h
.code:00401C49                 mov     ebp, dword_705314
.code:00401C4F                 add     ebp, 8
.code:00401C52                 mov     ebp, [ebp+11h]
.code:00401C55                 mov     dword ptr [ebp+10h], 8
.code:00401C5C                 mov     ebp, dword_705314
.code:00401C62                 add     ebp, 8
.code:00401C65                 push    dword ptr [ebp+0Dh]
.code:00401C68                 call    sub_56E513
.code:00401C6D                 mov     ebp, dword_705314
.code:00401C73                 add     ebp, 8
.code:00401C76                 mov     ebp, [ebp+11h]
.code:00401C79                 mov     edx, offset off_69AAA0 ; Str
.code:00401C7E                 lea     ecx, [ebp+8]    ; int
.code:00401C81                 call    sub_540558
.code:00401C86                 mov     ebp, dword_705314
.code:00401C8C                 add     ebp, 8
.code:00401C8F                 mov     ebp, [ebp+11h]
.code:00401C92                 mov     dword ptr [ebp+0Ch], 10000000h
.code:00401C99                 mov     ebp, dword_705314
.code:00401C9F                 add     ebp, 8
.code:00401CA2                 mov     ebp, [ebp+11h]
.code:00401CA5                 mov     dword ptr [ebp+10h], 10h
.code:00401CAC                 mov     ebp, dword_705314
.code:00401CB2                 add     ebp, 8
.code:00401CB5                 push    dword ptr [ebp+0Dh]
.code:00401CB8                 call    sub_56E513
.code:00401CBD                 mov     ebp, dword_705314
.code:00401CC3                 add     ebp, 8
.code:00401CC6                 mov     ebp, [ebp+11h]
.code:00401CC9                 mov     edx, offset off_695A52 ; Str
.code:00401CCE                 lea     ecx, [ebp+8]    ; int
.code:00401CD1                 call    sub_540558
.code:00401CD6                 mov     ebp, dword_705314
.code:00401CDC                 add     ebp, 8
.code:00401CDF                 mov     ebp, [ebp+11h]
.code:00401CE2                 mov     dword ptr [ebp+0Ch], 0C00000h
.code:00401CE9                 mov     ebp, dword_705314
.code:00401CEF                 add     ebp, 8
.code:00401CF2                 mov     ebp, [ebp+11h]
.code:00401CF5                 mov     dword ptr [ebp+10h], 20h
.code:00401CFC                 mov     ebp, dword_705314
.code:00401D02                 add     ebp, 8
.code:00401D05                 push    dword ptr [ebp+0Dh]
.code:00401D08                 call    sub_56E513
.code:00401D0D                 mov     ebp, dword_705314
.code:00401D13                 add     ebp, 8
.code:00401D16                 mov     ebp, [ebp+11h]
.code:00401D19                 mov     edx, offset off_68ED54 ; Str
.code:00401D1E                 lea     ecx, [ebp+8]    ; int
.code:00401D21                 call    sub_540558
.code:00401D26                 mov     ebp, dword_705314
.code:00401D2C                 add     ebp, 8
.code:00401D2F                 mov     ebp, [ebp+11h]
.code:00401D32                 mov     dword ptr [ebp+0Ch], 4
.code:00401D39                 mov     ebp, dword_705314
.code:00401D3F                 add     ebp, 8
.code:00401D42                 mov     ebp, [ebp+11h]
.code:00401D45                 mov     dword ptr [ebp+10h], 40h
.code:00401D4C                 mov     ebp, dword_705314
.code:00401D52                 add     ebp, 8
.code:00401D55                 push    dword ptr [ebp+0Dh]
.code:00401D58                 call    sub_56E513
.code:00401D5D                 mov     ebp, dword_705314
.code:00401D63                 add     ebp, 8
.code:00401D66                 mov     ebp, [ebp+11h]
.code:00401D69                 mov     edx, offset off_69312E ; Str
.code:00401D6E                 lea     ecx, [ebp+8]    ; int
.code:00401D71                 call    sub_540558
.code:00401D76                 mov     ebp, dword_705314
.code:00401D7C                 add     ebp, 8
.code:00401D7F                 mov     ebp, [ebp+11h]
.code:00401D82                 mov     dword ptr [ebp+0Ch], 80000000h
.code:00401D89                 mov     ebp, dword_705314
.code:00401D8F                 add     ebp, 8
.code:00401D92                 mov     ebp, [ebp+11h]
.code:00401D95                 mov     dword ptr [ebp+10h], 80h
.code:00401D9C                 mov     ebp, dword_705314
.code:00401DA2                 add     ebp, 8
.code:00401DA5                 push    dword ptr [ebp+0Dh]
.code:00401DA8                 call    sub_56E513
.code:00401DAD                 mov     ebp, dword_705314
.code:00401DB3                 add     ebp, 8
.code:00401DB6                 mov     ebp, [ebp+11h]
.code:00401DB9                 mov     edx, offset off_6916E6 ; Str
.code:00401DBE                 lea     ecx, [ebp+8]    ; int
.code:00401DC1                 call    sub_540558
.code:00401DC6                 mov     ebp, dword_705314
.code:00401DCC                 add     ebp, 8
.code:00401DCF                 mov     ebp, [ebp+11h]
.code:00401DD2                 mov     dword ptr [ebp+0Ch], 1
.code:00401DD9                 mov     ebp, dword_705314
.code:00401DDF                 add     ebp, 8
.code:00401DE2                 mov     ebp, [ebp+11h]
.code:00401DE5                 mov     dword ptr [ebp+10h], 100h
.code:00401DEC                 mov     ebp, dword_705314
.code:00401DF2                 add     ebp, 8
.code:00401DF5                 push    dword ptr [ebp+0Dh]
.code:00401DF8                 call    sub_56E513
.code:00401DFD                 mov     ebp, dword_705314
.code:00401E03                 add     ebp, 8
.code:00401E06                 mov     ebp, [ebp+11h]
.code:00401E09                 mov     edx, offset off_68C802 ; Str
.code:00401E0E                 lea     ecx, [ebp+8]    ; int
.code:00401E11                 call    sub_540558
.code:00401E16                 mov     ebp, dword_705314
.code:00401E1C                 add     ebp, 8
.code:00401E1F                 mov     ebp, [ebp+11h]
.code:00401E22                 mov     dword ptr [ebp+0Ch], 2
.code:00401E29                 mov     ebp, dword_705314
.code:00401E2F                 add     ebp, 8
.code:00401E32                 mov     ebp, [ebp+11h]
.code:00401E35                 mov     dword ptr [ebp+10h], 200h
.code:00401E3C                 mov     ebp, dword_705314
.code:00401E42                 add     ebp, 8
.code:00401E45                 push    dword ptr [ebp+0Dh]
.code:00401E48                 call    sub_56E513
.code:00401E4D                 mov     ebp, dword_705314
.code:00401E53                 add     ebp, 8
.code:00401E56                 mov     ebp, [ebp+11h]
.code:00401E59                 mov     edx, offset off_69776C ; Str
.code:00401E5E                 lea     ecx, [ebp+8]    ; int
.code:00401E61                 call    sub_540558
.code:00401E66                 mov     ebp, dword_705314
.code:00401E6C                 add     ebp, 8
.code:00401E6F                 mov     ebp, [ebp+11h]
.code:00401E72                 mov     dword ptr [ebp+0Ch], 1000000h
.code:00401E79                 mov     ebp, dword_705314
.code:00401E7F                 add     ebp, 8
.code:00401E82                 mov     ebp, [ebp+11h]
.code:00401E85                 mov     dword ptr [ebp+10h], 400h
.code:00401E8C                 mov     ebp, dword_705314
.code:00401E92                 add     ebp, 8
.code:00401E95                 push    dword ptr [ebp+0Dh]
.code:00401E98                 call    sub_56E513
.code:00401E9D                 mov     ebp, dword_705314
.code:00401EA3                 add     ebp, 8
.code:00401EA6                 mov     ebp, [ebp+11h]
.code:00401EA9                 mov     edx, offset off_694F68 ; Str
.code:00401EAE                 lea     ecx, [ebp+8]    ; int
.code:00401EB1                 call    sub_540558
.code:00401EB6                 mov     ebp, dword_705314
.code:00401EBC                 add     ebp, 8
.code:00401EBF                 mov     ebp, [ebp+11h]
.code:00401EC2                 mov     dword ptr [ebp+0Ch], 20000000h
.code:00401EC9                 mov     ebp, dword_705314
.code:00401ECF                 add     ebp, 8
.code:00401ED2                 mov     ebp, [ebp+11h]
.code:00401ED5                 mov     dword ptr [ebp+10h], 800h
.code:00401EDC                 mov     ebp, dword_705314
.code:00401EE2                 add     ebp, 8
.code:00401EE5                 push    dword ptr [ebp+0Dh]
.code:00401EE8                 call    sub_56E513
.code:00401EED                 mov     ebp, dword_705314
.code:00401EF3                 add     ebp, 8
.code:00401EF6                 mov     ebp, [ebp+11h]
.code:00401EF9                 mov     edx, offset off_690ADC ; Str
.code:00401EFE                 lea     ecx, [ebp+8]    ; int
.code:00401F01                 call    sub_540558
.code:00401F06                 mov     ebp, dword_705314
.code:00401F0C                 add     ebp, 8
.code:00401F0F                 mov     ebp, [ebp+11h]
.code:00401F12                 mov     dword ptr [ebp+0Ch], 8
.code:00401F19                 mov     ebp, dword_705314
.code:00401F1F                 add     ebp, 8
.code:00401F22                 mov     ebp, [ebp+11h]
.code:00401F25                 mov     dword ptr [ebp+10h], 1000h
.code:00401F2C                 mov     ebp, dword_705314
.code:00401F32                 add     ebp, 8
.code:00401F35                 push    dword ptr [ebp+15h]
.code:00401F38                 call    sub_56E513
.code:00401F3D                 mov     ebp, dword_705314
.code:00401F43                 add     ebp, 8
.code:00401F46                 mov     ebp, [ebp+19h]
.code:00401F49                 mov     edx, offset off_697A38 ; Str
.code:00401F4E                 lea     ecx, [ebp+8]    ; int
.code:00401F51                 call    sub_540558
.code:00401F56                 mov     ebp, dword_705314
.code:00401F5C                 add     ebp, 8
.code:00401F5F                 push    dword ptr [ebp+15h]
.code:00401F62                 call    sub_56E513
.code:00401F67                 mov     ebp, dword_705314
.code:00401F6D                 add     ebp, 8
.code:00401F70                 mov     ebp, [ebp+19h]
.code:00401F73                 mov     edx, offset off_69D178 ; Str
.code:00401F78                 lea     ecx, [ebp+8]    ; int
.code:00401F7B                 call    sub_540558
.code:00401F80                 mov     ebp, dword_705314
.code:00401F86                 add     ebp, 8
.code:00401F89                 push    dword ptr [ebp+15h]
.code:00401F8C                 call    sub_56E513
.code:00401F91                 mov     ebp, dword_705314
.code:00401F97                 add     ebp, 8
.code:00401F9A                 mov     ebp, [ebp+19h]
.code:00401F9D                 mov     edx, offset off_699E48 ; Str
.code:00401FA2                 lea     ecx, [ebp+8]    ; int
.code:00401FA5                 call    sub_540558
.code:00401FAA                 mov     ebp, dword_705314
.code:00401FB0                 add     ebp, 8
.code:00401FB3                 push    dword ptr [ebp+15h]
.code:00401FB6                 call    sub_56E513
.code:00401FBB                 mov     ebp, dword_705314
.code:00401FC1                 add     ebp, 8
.code:00401FC4                 mov     ebp, [ebp+19h]
.code:00401FC7                 mov     edx, offset off_69770A ; Str
.code:00401FCC                 lea     ecx, [ebp+8]    ; int
.code:00401FCF                 call    sub_540558
.code:00401FD4                 mov     ebp, dword_705314
.code:00401FDA                 add     ebp, 8
.code:00401FDD                 push    dword ptr [ebp+15h]
.code:00401FE0                 call    sub_56E513
.code:00401FE5                 mov     ebp, dword_705314
.code:00401FEB                 add     ebp, 8
.code:00401FEE                 mov     ebp, [ebp+19h]
.code:00401FF1                 mov     edx, offset off_692BA4 ; Str
.code:00401FF6                 lea     ecx, [ebp+8]    ; int
.code:00401FF9                 call    sub_540558
.code:00401FFE                 mov     ebp, dword_705314
.code:00402004                 add     ebp, 8
.code:00402007                 push    dword ptr [ebp+15h]
.code:0040200A                 call    sub_56E513
.code:0040200F                 mov     ebp, dword_705314
.code:00402015                 add     ebp, 8
.code:00402018                 mov     ebp, [ebp+19h]
.code:0040201B                 mov     edx, offset off_68FA1C ; Str
.code:00402020                 lea     ecx, [ebp+8]    ; int
.code:00402023                 call    sub_540558
.code:00402028                 mov     ebp, dword_705314
.code:0040202E                 add     ebp, 8
.code:00402031                 push    dword ptr [ebp+15h]
.code:00402034                 call    sub_56E513
.code:00402039                 mov     ebp, dword_705314
.code:0040203F                 add     ebp, 8
.code:00402042                 mov     ebp, [ebp+19h]
.code:00402045                 mov     edx, offset off_6953C2 ; Str
.code:0040204A                 lea     ecx, [ebp+8]    ; int
.code:0040204D                 call    sub_540558
.code:00402052                 mov     ebp, dword_705314
.code:00402058                 add     ebp, 8
.code:0040205B                 push    dword ptr [ebp+15h]
.code:0040205E                 call    sub_56E513
.code:00402063                 mov     ebp, dword_705314
.code:00402069                 add     ebp, 8
.code:0040206C                 mov     ebp, [ebp+19h]
.code:0040206F                 mov     edx, offset off_699AE0 ; Str
.code:00402074                 lea     ecx, [ebp+8]    ; int
.code:00402077                 call    sub_540558
.code:0040207C                 mov     ebp, dword_705314
.code:00402082                 add     ebp, 8
.code:00402085                 push    dword ptr [ebp+15h]
.code:00402088                 call    sub_56E513
.code:0040208D                 mov     ebp, dword_705314
.code:00402093                 add     ebp, 8
.code:00402096                 mov     ebp, [ebp+19h]
.code:00402099                 mov     edx, offset off_695382 ; Str
.code:0040209E                 lea     ecx, [ebp+8]    ; int
.code:004020A1                 call    sub_540558
.code:004020A6                 mov     ebp, dword_705314
.code:004020AC                 add     ebp, 8
.code:004020AF                 push    dword ptr [ebp+15h]
.code:004020B2                 call    sub_56E513
.code:004020B7                 mov     ebp, dword_705314
.code:004020BD                 add     ebp, 8
.code:004020C0                 mov     ebp, [ebp+19h]
.code:004020C3                 mov     edx, offset off_692706 ; Str
.code:004020C8                 lea     ecx, [ebp+8]    ; int
.code:004020CB                 call    sub_540558
.code:004020D0                 mov     ebp, dword_705314
.code:004020D6                 add     ebp, 8
.code:004020D9                 push    dword ptr [ebp+15h]
.code:004020DC                 call    sub_56E513
.code:004020E1                 mov     ebp, dword_705314
.code:004020E7                 add     ebp, 8
.code:004020EA                 mov     ebp, [ebp+19h]
.code:004020ED                 mov     edx, offset off_6897C6 ; Str
.code:004020F2                 lea     ecx, [ebp+8]    ; int
.code:004020F5                 call    sub_540558
.code:004020FA                 mov     ebp, dword_705314
.code:00402100                 add     ebp, 8
.code:00402103                 push    dword ptr [ebp+15h]
.code:00402106                 call    sub_56E513
.code:0040210B                 mov     ebp, dword_705314
.code:00402111                 add     ebp, 8
.code:00402114                 mov     ebp, [ebp+19h]
.code:00402117                 mov     edx, offset off_69784A ; Str
.code:0040211C                 lea     ecx, [ebp+8]    ; int
.code:0040211F                 call    sub_540558
.code:00402124                 mov     ebp, dword_705314
.code:0040212A                 add     ebp, 8
.code:0040212D                 push    dword ptr [ebp+15h]
.code:00402130                 call    sub_56E513
.code:00402135                 mov     ebp, dword_705314
.code:0040213B                 add     ebp, 8
.code:0040213E                 mov     ebp, [ebp+19h]
.code:00402141                 mov     edx, offset off_69790A ; Str
.code:00402146                 lea     ecx, [ebp+8]    ; int
.code:00402149                 call    sub_540558
.code:0040214E                 mov     ebp, dword_705314
.code:00402154                 add     ebp, 8
.code:00402157                 push    dword ptr [ebp+15h]
.code:0040215A                 call    sub_56E513
.code:0040215F                 mov     ebp, dword_705314
.code:00402165                 add     ebp, 8
.code:00402168                 mov     ebp, [ebp+19h]
.code:0040216B                 mov     edx, offset off_698C36 ; Str
.code:00402170                 lea     ecx, [ebp+8]    ; int
.code:00402173                 call    sub_540558
.code:00402178                 push    dword_705310
.code:0040217E                 call    sub_56E513
.code:00402183                 mov     ebp, dword_705314
.code:00402189                 mov     dword ptr [ebp+0Ch], 32h
.code:00402190                 mov     edx, offset aButton ; "Button"
.code:00402195                 lea     ecx, [ebp+8]    ; int
.code:00402198                 call    sub_540558
.code:0040219D                 mov     byte ptr [ebp+14h], 1
.code:004021A1                 mov     dword ptr [ebp+10h], 59h
.code:004021A8                 add     ebp, 8
.code:004021AB                 push    dword ptr [ebp+0Dh]
.code:004021AE                 call    sub_56E513
.code:004021B3                 mov     ebp, dword_705314
.code:004021B9                 add     ebp, 8
.code:004021BC                 mov     ebp, [ebp+11h]
.code:004021BF                 mov     edx, offset off_696176 ; Str
.code:004021C4                 lea     ecx, [ebp+8]    ; int
.code:004021C7                 call    sub_540558
.code:004021CC                 mov     ebp, dword_705314
.code:004021D2                 add     ebp, 8
.code:004021D5                 mov     ebp, [ebp+11h]
.code:004021D8                 mov     dword ptr [ebp+0Ch], 200h
.code:004021DF                 mov     ebp, dword_705314
.code:004021E5                 add     ebp, 8
.code:004021E8                 mov     ebp, [ebp+11h]
.code:004021EB                 mov     dword ptr [ebp+10h], 1
.code:004021F2                 mov     ebp, dword_705314
.code:004021F8                 add     ebp, 8
.code:004021FB                 push    dword ptr [ebp+0Dh]
.code:004021FE                 call    sub_56E513
.code:00402203                 mov     ebp, dword_705314
.code:00402209                 add     ebp, 8
.code:0040220C                 mov     ebp, [ebp+11h]
.code:0040220F                 mov     edx, offset off_6939FE ; Str
.code:00402214                 lea     ecx, [ebp+8]    ; int
.code:00402217                 call    sub_540558
.code:0040221C                 mov     ebp, dword_705314
.code:00402222                 add     ebp, 8
.code:00402225                 mov     ebp, [ebp+11h]
.code:00402228                 mov     dword ptr [ebp+0Ch], 100h
.code:0040222F                 mov     ebp, dword_705314
.code:00402235                 add     ebp, 8
.code:00402238                 mov     ebp, [ebp+11h]
.code:0040223B                 mov     dword ptr [ebp+10h], 2
.code:00402242                 mov     ebp, dword_705314
.code:00402248                 add     ebp, 8
.code:0040224B                 push    dword ptr [ebp+0Dh]
.code:0040224E                 call    sub_56E513
.code:00402253                 mov     ebp, dword_705314
.code:00402259                 add     ebp, 8
.code:0040225C                 mov     ebp, [ebp+11h]
.code:0040225F                 mov     edx, offset off_68A2BC ; Str
.code:00402264                 lea     ecx, [ebp+8]    ; int
.code:00402267                 call    sub_540558
.code:0040226C                 mov     ebp, dword_705314
.code:00402272                 add     ebp, 8
.code:00402275                 mov     ebp, [ebp+11h]
.code:00402278                 mov     dword ptr [ebp+0Ch], 1
.code:0040227F                 mov     ebp, dword_705314
.code:00402285                 add     ebp, 8
.code:00402288                 mov     ebp, [ebp+11h]
.code:0040228B                 mov     dword ptr [ebp+10h], 4
.code:00402292                 mov     ebp, dword_705314
.code:00402298                 add     ebp, 8
.code:0040229B                 push    dword ptr [ebp+0Dh]
.code:0040229E                 call    sub_56E513
.code:004022A3                 mov     ebp, dword_705314
.code:004022A9                 add     ebp, 8
.code:004022AC                 mov     ebp, [ebp+11h]
.code:004022AF                 mov     edx, offset off_698054 ; Str
.code:004022B4                 lea     ecx, [ebp+8]    ; int
.code:004022B7                 call    sub_540558
.code:004022BC                 mov     ebp, dword_705314
.code:004022C2                 add     ebp, 8
.code:004022C5                 mov     ebp, [ebp+11h]
.code:004022C8                 mov     dword ptr [ebp+0Ch], 2000h
.code:004022CF                 mov     ebp, dword_705314
.code:004022D5                 add     ebp, 8
.code:004022D8                 mov     ebp, [ebp+11h]
.code:004022DB                 mov     dword ptr [ebp+10h], 8
.code:004022E2                 mov     ebp, dword_705314
.code:004022E8                 add     ebp, 8
.code:004022EB                 push    dword ptr [ebp+0Dh]
.code:004022EE                 call    sub_56E513
.code:004022F3                 mov     ebp, dword_705314
.code:004022F9                 add     ebp, 8
.code:004022FC                 mov     ebp, [ebp+11h]
.code:004022FF                 mov     edx, offset off_69A590 ; Str
.code:00402304                 lea     ecx, [ebp+8]    ; int
.code:00402307                 call    sub_540558
.code:0040230C                 mov     ebp, dword_705314
.code:00402312                 add     ebp, 8
.code:00402315                 mov     ebp, [ebp+11h]
.code:00402318                 mov     dword ptr [ebp+0Ch], 1003h
.code:0040231F                 mov     ebp, dword_705314
.code:00402325                 add     ebp, 8
.code:00402328                 mov     ebp, [ebp+11h]
.code:0040232B                 mov     dword ptr [ebp+10h], 10h
.code:00402332                 push    dword_705310
.code:00402338                 call    sub_56E513
.code:0040233D                 mov     ebp, dword_705314
.code:00402343                 mov     dword ptr [ebp+0Ch], 33h
.code:0040234A                 mov     byte ptr [ebp+14h], 1
.code:0040234E                 mov     edx, offset aB_10 ; "B"
.code:00402353                 lea     ecx, [ebp+8]    ; int
.code:00402356                 call    sub_540558
.code:0040235B                 mov     dword ptr [ebp+10h], 5Ah
.code:00402362                 add     ebp, 8
.code:00402365                 push    dword ptr [ebp+0Dh]
.code:00402368                 call    sub_56E513
.code:0040236D                 mov     ebp, dword_705314
.code:00402373                 add     ebp, 8
.code:00402376                 mov     ebp, [ebp+11h]
.code:00402379                 mov     edx, offset off_69A590 ; Str
.code:0040237E                 lea     ecx, [ebp+8]    ; int
.code:00402381                 call    sub_540558
.code:00402386                 mov     ebp, dword_705314
.code:0040238C                 add     ebp, 8
.code:0040238F                 mov     ebp, [ebp+11h]
.code:00402392                 mov     dword ptr [ebp+0Ch], 1003h
.code:00402399                 mov     ebp, dword_705314
.code:0040239F                 add     ebp, 8
.code:004023A2                 mov     ebp, [ebp+11h]
.code:004023A5                 mov     dword ptr [ebp+10h], 10h
.code:004023AC                 push    dword_705310
.code:004023B2                 call    sub_56E513
.code:004023B7                 mov     ebp, dword_705314
.code:004023BD                 mov     dword ptr [ebp+0Ch], 49h
.code:004023C4                 mov     byte ptr [ebp+14h], 1
.code:004023C8                 mov     dword ptr [ebp+10h], 5Bh
.code:004023CF                 mov     edx, offset off_69635E ; Str
.code:004023D4                 lea     ecx, [ebp+8]    ; int
.code:004023D7                 call    sub_540558
.code:004023DC                 add     ebp, 8
.code:004023DF                 push    dword ptr [ebp+0Dh]
.code:004023E2                 call    sub_56E513
.code:004023E7                 mov     ebp, dword_705314

Kolejno ustawiana jest wartość rejestru EBP, nie zważając na to, że z 20 razy jest ta sama operacja wykonywana, być może twórcy PureBasic-a  doczytali o loop unrollingu i wdrożyli go w życie z dodatkowymi benefitami…

2. Techniki czyszczenia stosu dla cdecl

Konwencja wywoływania funkcji cdecl bazuje na tym, że parametry są kolejno wrzucane na stos serią instrukcji push, po czym następuje wywołanie funkcji instrukcją call, po której wskaźnik stosu np. ESP jest korygowany o rozmiar wrzuconych wcześniej parametrów np. przez add esp,8 (dla dwóch 4 bajtowych parametrów etc.).

Różne kompilatory różnie radzą sobie z korektą stosu, jedne po każdym wywołaniu funkcji w konwencji cdecl korygują stos, inne potrafią inteligentnie obliczyć rozmiar parametrów i skorygować stos po całej serii wywołań wielu funkcji w konwencji cdecl (np. LCC), a jeszcze inne kompilatory robią to tak:

.code:00445E0D                 push    eax
.code:00445E0E                 push    [esp+14h+var_10] ; lpMem
.code:00445E12                 call    sub_59A300
.code:00445E17                 pop     eax
.code:00445E18                 add     esp, 4
.code:00445E1B                 xor     eax, eax

Mówiąc w skrócie po wykonaniu call-a (2 parametry, czyli 8 bajtów na stosie zostało do tego wykorzystanych) stos korygowany jest przez instrukcję pop eax (która działa jak add esp,4), po czym stos korygowany jest ponownie instrukcją add esp,4, a po niej wartość rejestru EAX, który jak można by się było domyśleć miał być do czegoś wykorzystany – jest zerowany! PureBasic – PureMagic 🙂

3. Sprawdzanie flag

W sumie można by to zaliczyć do optymalizacji, gdyby w ogóle kompilator jakieś stosował:

.code:004B42B2                 call    sub_50F9BC
.code:004B42B7                 and     eax, eax
.code:004B42B9                 jz      loc_4B43D4
.code:004B42D0                 call    sub_56E5C6
.code:004B42D5                 or      eax, eax
.code:004B42D7                 jz      loc_4B43CF
.text:0065062E                 test    eax, eax
.text:00650630                 jz      short loc_65063B

Jak widać kompilator PureBasic w zależności od humoru stosuje 3 metody sprawdzania czy rejestr jest wyzerowany (z czego 2 nie są rekomendowane, gdyż to operacje zapisu czyli and i or, instrukcja test jedynie ustawia flagi).

4. Konwencja „zerocall”

W czasach DOS-u, częstą praktyką była konwencja wywoływania funkcji, w której ustawiano flagę carry (przeniesienia) instrukcją STC lub ją zerowano instrukcją CLC w zależności od wartości zwracanej przez funkcję (true / false). Przypadki użycia takich technik widziałem również w kilku programach na Windows, jednak to taka rzadkość, że można przypuszczać, że to twór programistów mających korzenie programistyczne w DOS-ie.

Wykorzystanie flagi carry miało swoje plusy, gdyż po wywołaniu funkcji nie trzeba było sprawdzać wyniku funkcji np. przez test eax,eax, a jedynie wykonać instrukcję skoku JC lub JNC.

PureBasic stosuje unikalną konwencję, którą nazwałem zerocall:

.text:0059A142 loc_59A142:                             ; CODE XREF: sub_59A110+22j
.text:0059A142                 pop     ebx
.text:0059A143                 xor     eax, eax
.text:0059A145                 test    eax, eax
.text:0059A147                 retn
.text:0059A147 sub_59A110      endp
...
.code:00413667                 mov     edx, offset ValueName
.code:0041366C                 pop     ecx
.code:0041366D                 call    sub_59A110
.code:00413672                 jz      short loc_41367B

Czyli funkcja sama w sobie sprawdza wynik i ustawia flagę zerową, można powiedzieć, że jest to technologia unikalna dla PB, gdyż nigdy wcześniej nie spotkałem się z taką innowacją.

Co ciekawe miałoby to jedynie sens w przypadku optymalizacji pod względem rozmiaru i w przypadku zastosowanie tej techniki do wszystkich wygenerowanych funkcji, a tak niestety nie jest.

5. Zerowanie pustej pamięci

W sumie ten kod zainspirował mnie do kolejnego artykułu po kompilatorze języka Go. Generował go kompilator PB w wersji od 4.xx do 5.30.

.code:00401000 start           proc near
.code:00401000
.code:00401000 var_C           = dword ptr -0Ch
.code:00401000 var_8           = dword ptr -8
.code:00401000
.code:00401000                 push    1C20h           ; Size
.code:00401005                 push    0               ; Val
.code:0040100A                 push    offset hHeap    ; Dst
.code:0040100F                 call    memset

Krótki opis. Zerowana jest pamięć od zmiennej hHeap, która znajduje się w sekcji danych „.data”. Zerowany bufor pamięci ma rozmiar 0x1C20 bajtów.

Spoglądając na deadlisting można zauważyć, że ten obszar jest pusty! A do tego jego większa część znajduje się w zakresie sekcji „.data”, która jest niezainicjalizowana.

Gwoli wyjaśnienia sekcja „.data” posiada ustawiony rozmiar fizyczny oraz rozmiar wirtualny, który przekracza rozmiar fizyczny co pozwala na zaoszczędzenie miejsca na dysku plikom, które chcą mieć zaalokowane duże obszary pamięci.

System Windows niezainicjalizowane obszary pamięci w sekcjach zawsze wypełnia zerami.

Zerowany obszar w plikach PureBasic jest zatem wypełniony 0x00 od samego początku i nie ma potrzeby dodatkowo go zerować, całej sytuacji smaczku dodaje jeszcze fakt, że obszar ten wykorzystywany jest do trzymania zmiennych globalnych i kompilator zamiast zapisać wartości tych zmiennych od razu do pliku, najpierw generuje kod, który zeruje cały ten obszar, a później dodatkowo generuje instrukcje, które ustawiają te wartości, czyli 2 razy marnowany jest czas procesora po uruchomieniu takiej aplikacji, podczas gdy cały ten proces można zrealizować na etapie kompilacji.

Konkluzje

PureBasic to całkiem ciekawe narzędzie, ale jakość generowanego kodu pozostawia wiele do życzenia i nie zdecydowałbym się na jego używanie do wysokowydajnych aplikacji, poza tym fundamentalne braki w wiedzy twórców kompilatora, w tak podstawowych kwestiach jak alokacja pamięci czy poprawne wykorzystanie instrukcji procesora x86, sprawiają, że można mieć wątpliwości co do samej jakości kodu jak i innych technologii użytych w tym narzędziu.