Najlepsze praktyki optymalizacji i pozycjonowania SEO w 2022 roku

Najlepsze praktyki SEO w 2022 roku do pozycjonowania i optymalizacji stron internetowych wyniesione z własnego doświadczenia. Wady, zalety i narzędzia do SEO.

Jak nie pozycjonować strony internetowej?

Zacznę od tego czego nie powinieneś robić. A na pewno nie powinieneś zatrudniać żadnej firmy od pozycjonowania SEO, chyba że chcesz stracić pieniądze. A gwarantuję, że stracisz, a oni będą Cię co miesiąc doić finansowo jak krowę.

Dojenie krowy
Firma SEO, która doi cię z pieniędzy

Jak działają polskie firmy SEO w 99%?

Polskie firmy SEO to dno spamerskiego mułu. Myślą, że są cwaniakami, bo kupili spamerskie narzędzia w stylu Xrumer za gruby hajs lub uprawiają tzw. marketing szeptany spamując na forach (które de facto nie są już tak popularne jak kiedyś) albo wcisną Ci bajeczkę, że znają tajemny składnik na sos SEO (tzw. SEO juice), ale co to jest, to Ci już nie powiedzą.

SEO Juice - tajemniczy składnik optymalizacji SEO
SEO Juice – tajemniczy składnik optymalizacji SEO

Miałem to szczęście, że nigdy nie musiałem zatrudniać tego typu firmy, jednak doradzałem klientom, którzy wpadli w tą pułapkę i słono za to zapłacili.

Metody SEO polskich firm optymalizacyjnych

Kilka taktyk SEO, które zauważyłem na przestrzeni lat wykorzystywanych przez firmy SEO po wykupieniu jakichś gównopakietów typu SEO VIP Turbo Shit Tier Google Number 1:

  1. Każą Ci poprawić dwa zdania na stronie, dla “optymalizacji seo”, dorzucą ze dwa dodatkowe zdania od siebie i włączą darmową wtyczkę do optymalizacji typu Yoast jeśli masz bloga na WordPressie. Jak będziesz miał pecha to dorzucą Ci gdzieś w footerze, mało widocznego linka do ich własnej strony.
  2. Zakładają blogi pod różnymi domenami, na różnych adresach IP, są to tzw. spam farmy lub jak to oni górnolotnie lubią pisać – zaplecze SEO. Do czego to służy? Czytaj dalej!
  3. Zatrudniają studenta, który pisze im wpisy na te spam farmy ze słowami kluczowymi pod klienta np. “najlepsza pralka” i linkiem do twojej strony. W 99% przypadków klient nawet nie wie (i celowo się tego nie mówi klientom) na jakim blogu zostały umieszczone artykuły linkującego do jego strony, a na tym samym blogu będą również inne spamowe wpisy dla innych klientów z zupełnie innych dziedzin. Przykładowo jak reklamujesz sklep internetowy to spodziewaj się, że znajdziesz się obok reklam grzybków konserwowych, salonów kosmetycznych i producentów kostki brukowej. Wujek Google od razu wykryje szwindel, a jak myślisz, że twoja firma SEO i pakiet za 1000 PLN na miesiąc są sprytniejsze od algorytmów Google za miliony dolarów to znaczy, że jesteś głupcem.
  4. Automatycznie wysyłają ten sam artykuł na wszystkie te spamowe blogi, dodatkowo używają np. randomowych zwrotów w formie template np. {Dzień dobry|Cześć|Siema}, co sprawia że artykuł jest trochę inny, czasami korzystają do tego z tzw. text spinnerów, które to robią automatycznie. Obecnie wykorzystywane są też narzędzia do przepisywania tekstów, wykorzystujące sztuczną inteligencję, jak np. znakomity QuillBot
  5. Udają, że nie wiedzą, że firma taka jak Google potrafi wykrywać spam farmy od lat i nawet posiada na to patent, udajmy, że tego nie widzimy… Adresy IP takich spam farm są na czarnych listach wujka Googla i to, że znajduje się tam artykuł z linkiem dofollow do twojej witryny nie zmienia absolutnie twojej pozycji w wynikach wyszukiwania, może chwilowo, dopóki farma nie zostanie wciągnięta na czarną listę.
  6. Odkryli magię marketingu szeptanego i wygląda to tak, że tworzą spamerskie konta na forach internetowych, jedna osoba pyta o coś np. “Jak szybko wypozycjonować moją stronę w Google?”, a druga za np. tydzień odpowiada i to całkiem fajnie i sensownie, bez żadnych linków, wszystko wygląda OK. Są na tyle cwani, że za dwa tygodnie do tej odpowiedzi już jest dodany link do ich klienta, robią to po to, żeby administracja na początku nie usunęła im tego spamu i żeby wszystko wyglądało ehem organicznie. Pozdrawiam idiotów spamujących tak moje forum programistyczne.
  7. Płacisz tym januszom seo co miesiąc za ten spam
  8. ???
  9. Profit (dla nich)
  10. Jak rezygnujesz z subskrypcji to Ci odcinają te gównoblogowe artykuły, spam farmy i tracisz “moc seo” (której i tak nie miałeś)
  11. Zapomniałbym, że przy próbie zrezygnowania z intratnej subskrypcji ich spamerskich usług możesz się spodziewać natarczywych telefonów od samego prezesa, który będzie Cię namawiał do pozostania wizjami ich nowych technologii spamerskich, po groźby prawnicze dotyczące zawiłych warunków zerwania umowy z np. 6 miesięcznym okresem wypowiedzenia. Tak łatwo nie oddadzą Ci SWOICH zarobionych pieniędzy. Nie mów, że nie ostrzegałem.

To niemożliwe, nikt tak nie robi już od dawna!

Nie wierzysz, że takie praktyki są do dzisiaj w 2022 roku stosowane? Poczytaj sobie ogłoszenia (świeże) na forum Pozycjonowanie i Optymalizacja, gdzie piszą również oficjalnie firmy SEO:

Optymalizacja i pozycjonowanie SEO, marketing szeptany.
Spam, spam, spam…

Jak szybko wypozycjonować swoją stronę?

To trochę klik-baitowy nagłówek, bo szybko może to zrobić ktoś kto ma doświadczenie, jeśli jednak masz czas i poświecisz go na to, o czym piszę – zapewniam będziesz o dobre kilka kroków przed konkurencją.

  1. Zacznij od dobrej podstawy. Kup porządny serwer lub hosting, nie jedź na darmowych hostingach, szybko odbijesz się od ich darmowych limitów i niedociągnięć. Google faworyzuje strony, które szybko się otwierają i do których jest szybki dostęp (czytaj fizycznie serwer jest blisko Ciebie). Jeśli masz firmę w Polsce – nie kupuj serwera czy hostingu w Ameryce, jeśli celujesz na cały świat – kup tam, skąd spodziewasz się swoich klientów. Do platform blogowych jak np. WordPress istnieją specjalistyczne hostingi, które stosują np. dyski NVM, mają włączony cache, obsługują najnowszy protokół HTTP/3, czy PHP 8 np. fajnie prezentuje się Cyberfolks (nie płacą mi).
  2. Dobry kontent, cały czas rozbudowa strony, a nie rzucenie dwóch podstron raz na 5 lat. Nie masz czasu, chęci? Zatrudnij kogoś do copywritingu, na Freelancerze znajdziesz ludzi, którzy to zrobią za grosze. Inwestuj we własną stronę, a nie spam farmy.
  3. Strona zoptymalizowana pod urządzenia mobilne jest faworyzowana w SERPach Google, zrób sobie ją np. w Bootstrap i przetestuj w narzędziu od Google, popraw wszystkie błędy
  4. Wiele wersji językowych połączonych hreflangiem Tell Google about localized versions of your page
  5. Tagi open graph i twitterowe Webmasters – Sharing – Documentation – Facebook for Developers. Co to sprawy? Że jak ktoś wklei linka do twojej strony na tych platformach to jednocześnie zostanie wyświetlony ładny obrazek oznaczony np. w tagach og:image
  6. Prosta struktura HTML, sekcje <nav> <article>, nagłówki <h1> (tylko jeden) i pod spodem wiele <h2> i <h3>
  7. Dobrze nazwane (to też ważne, nie używaj spacji ani polskich znaczków diakrytycznych) i opisane obrazki z atrybutem ALT (alternatywny opis obrazka dla osób niewidomych i robotów wyszukiwarek), co sprawi, że twój obrazek będzie częściej wyświetlany w wynikach wyszukiwania <img src=”…/jak-zoptymalizowac-strone-pod-seo.png” alt=”Jak zoptymalizować stronę pod SEO”>
  8. Dodanie tagów z Home – schema.org (microdata) np. Article – schema.org Type czy np. reviews (gwiazdki), które wyświetlane są w wynikach wyszukiwania
  9. Dbanie o to, żeby np. linki do materiałów czy zdjęć były CAŁY czas ok Screaming Frog SEO Spider, a uwierz mi, że linki na stronach cały czas są zmieniane i trzeba tego pilnować
  10. Dodanie tagów nofollow do linków zewnętrznych, żeby nam nie zjadały “seo juice” <a href="..." rel="nofollow"...
  11. Optymalizacja rozmiaru strony i obrazków dla szybszego ładowania np. Pinga pingo, fast image optimizer lub wykorzystanie gotowych komponentów do ich generowania np. yii2-imgopt, oraz wykorzystanie tzw. lazy loading, czyli leniwe ładowanie obrazków. W przyszłości wykorzystanie zoptymalizowanych formatów pod strony internetowe np. format .webp lub/i .avif
  12. Dobra struktura linków https://domena.com/artykuly/najlepsze-praktyki-seo a nie https://domena.com/article.php?id=39481
  13. Poprawne meta tagi (jedynie description, keywords można olać), tytuł strony, oraz ich długość (ScreamingFrog to wychwyci)
  14. Wrzucanie newsów z linkami na swoje profile social media (Twitter, Pinterest, Facebook itd.)
  15. Monitoring swojej strony na Google Search Console (błędy, błędy mobilne, szybkość działania strony)
  16. Monitorowanie strony we wbudowanym narzędziu w Google Chrome – Lighhouse (Klik prawym na stronę → Zbadaj Element → Zakładka Inspekcja)
  17. Jeśli masz siły i chęci to dorobienie wersji mobilnej AMP, która jest faworyzowana w wynikach wyszukiwania (np. wykorzystując wtyczki, nie wiem, do WordPressa). Nieaktualne, AMP upadło, nie marnuj czasu!
  18. Zainwestuj w tutoriale wideo prezentujące twoje produkty czy usługi, biorąc pod uwagę ceny takich usług na portalach jak Freelancer czy Fiverr nie zapłacisz dużo. Film możesz wgrać nie tylko na YouTube ale też na inne portale np. Vimeo. W każdym filmiku w opisie dodaj link do własnej strony.
  19. Zrób prezentację swojego produktu, usługi czy instrukcji obsługi i wrzuć na strony typu Slideshare.
  20. I coś o czym wielu ludzi zapomina i kompletnie o to nie dba. W miarę możliwości sukcesywne zwiększanie ilości linków zewnętrznych na TEMATYCZNIE powiązanych stronach do naszej strony DOFOLLOW (czyli bez tagu rel="nofollow"). Jak je zdobyć? Wymiana linków, kupno linków (a jak), gościnne artykuły, przeglądy, wywiady, notatki prasowe. A najlepszym sposobem jest mieć po prostu dobry produkt, o którym inni sami będą pisali. Zdobywanie zewnętrznych linków jest cholernie trudne i pracochłonne, ale to podstawa działania wyszukiwarki i pozycji naszej strony od samego początku istnienia Google. Mogę polecić świetną prezentację Pana Szymona Słowika odnośnie tego tematu (wiele ciekawych smaczków):
Nieoczekiwane taktyki link buildingu.

Czy warto kupować drogie, komercyjne narzędzia do SEO?

To zależy, jeśli pracujesz nad własną stroną uważam, że nie, chyba, że jednorazowo. Jeśli zajmujesz się SEO i zarządzasz kilkoma stronami lub stronami klientów – jak najbardziej. Samo kupno narzędzi do analizy SEO nie sprawi, że nagle twoja strona wyskoczy na pierwsze miejsce w wynikach wyszukiwania Google.

Najpopularniejsze narzędzia do analizy konkurencji i własnych projektów to m.in.:

Nie namawiam do piractwa, ale jeśli uważasz, że ceny niektórych z tych narzędzi są z kosmosu i że ryją swoimi botami po twoich stronach dzień w dzień jedynie dla własnego zysku nie dając niczego w zamian, zainteresuj się czymś takim jak usługi group buy, gdzie jedna osoba kupuje licencję na pakiet usług i odsprzedaje do nich dostęp w ramach własnego konta. Teraz już wiesz.

Dwa podstawowe błędy w SEO

Pierwszy podstawowy błąd jaki widzę to brak rozwijania strony, raz ktoś postawi i wisi tak 10 lat to samo, bez dodatkowych treści, bez poprawy błędów językowych, bez poprawy linków, bez dbania o cokolwiek. Technologia stron ciągle ewoluuje, ciągle dodawane są nowe elementy, o które warto wzbogacić własną stronę.

Nie pozwól, żeby twoja strona wyglądała jak stara stodoła…

Drugi błąd to brak zrozumienia, że potrzebne są linki zewnętrzne kierujące do twojej strony i że na tej zasadzie od samego początku funkcjonowała wyszukiwarka Google. Warto poczytać o algorytmie PageRank. Obecnie analizowane są zapewne sygnały z wielu źródeł, nie tylko linki prowadzące do naszej witryny, ale np. linki z social media (które niekoniecznie mają atrybuty dofollow). Piszę o tym, bo spotkałem się z sytuacją, gdy ktoś był zaskoczony, że zrobił piękną stronę, a jest na dnie wyników wyszukiwania. Po krótkiej rozmowie i analizie ile linków prowadzi do jego strony, wyszło że zero, bo myślał, że sama strona wystarczy. W dwa tygodnie i moich sugestiach udało mu się zdobyć 5 linków zewnętrznych, co w jego branży i konkurencji pomogło mu się wybić na górę wyników wyszukiwania, niby 5 linków to niewiele, a zmienia bardzo dużo.

Jak mówi przysłowie – jak dbasz, tak masz i to doskonale sprawdza się w optymalizacji SEO.

Masz uwagi, ciekawe narzędzia SEO, coś przekręciłem – skomentuj i zostaw linka.

Optymalizacja kodu assemblera pod względem rozmiaru

Zapraszam do przeczytania mojego małego artykułu na temat optymalizacji kodu assemblera pod względem wielkości. Kilka przydatnych sztuczek przydaje się przy reversingu aplikacji, gdy trzeba zmieścić kod w ograniczonej przestrzeni lub przy kodowaniu shellcodów.

Artykuł dostępny na:

https://dev.to/pelock/assembly-code-size-optimization-tricks-2abd

PS. Polecam również prezentujący różne ciekawe sztuczki nieosiągalne dla kompilatorów HLL

http://mark.masmcode.com/

Optymalizacja kodu

binaryMało kto pisze tak ciekawie o optymalizacji kodu i wykorzystaniu nowych instrukcji procesorów jak Wojtek Muła, dlatego jeśli jesteś jedną z nielicznych osób, którą interesują takie kwestie jak niskopoziomowa optymalizacja kodu, to koniecznie zajrzyj do notesu Wojtka, w którym zamieścił wiele ciekawych przykładów:

http://wm.ite.pl/notesen.html

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.

Technologiczna bieda kompilatora Go

Lubię babrać się w low levelu, to zarówno moja pasja jak i praca. Jestem w trakcie testowania nowego exe protectora i jak to zwykle bywa – testuje nim co popadnie, wliczając wszelkie nietypowe pliki w formacie PE, w tym pliki z różnych kompilatorów i środowisk programistycznych jak np. DMD, Lazarus / FPC, PureBasic, a nawet takie antyki jak WATCOM.

Różne kompilatory stosują całą mase magicznych zabiegów, aby czasami nieświadomie uniemożliwić zabezpieczenie wygenerowanego pliku PE EXE, a ja staram się to ładnie obsłużyć, ominąć, hookować etc.

Dzisiaj przysiadłem sobie do kompilatora języka wprost od Google, czyli – Go.

…i zdębiałem. Ściągnąłem paczkę v1.3.1 dla Windows w wersji 32 bitowej.

O ile protector poradził sobie bez zająknięcia z testowym zabezpieczeniem pliku kompilatora – go.exe, o tyle nie byłbym sobą jeśli bym nie zajrzał do środka, jak strukturalnie zbudowany jest plik z Go (biorąc pod uwagę, że kompilator Go zbudowany jest ze źródeł w C oraz samym języku Go).

A w środku szalone lata 90 :), spójrzmy na to razem:

1. Mania używania CLD

CLD to instrukcja procesora ustawiająca flagę kierunku wykonywania operacji takich jak rep stosb, rep movsd, scasb, jej zresetowanie powoduje, że operacje te wykonują się bajt po bajcie w kolejności rosnącej, jej ustawienie powoduje, że operacje te wykonują się odwrotnie (czyli od końca do przodu).

Problem w tym, że na systemach Windows ta flaga jest domyślnie już wyzerowana, a funkcje WinApi nigdy jej nie ustawiają, rzadkością są w ogóle przypadki, że kiedykolwiek jest ona ustawiana instrukcją STD, chyba jedynie w jakichś egzotycznych przykładach napisanych w assemblerze.

W binarce go.exe znajdziemy tego całą masę:

.text:00473CF8                 call    runtime_emptyfunc
.text:00473CFD                 cld
.text:00473CFE                 call    runtime_check

W 1 bloku kodu (a co, kto bogatemu zabroni):

.text:004BCB1E                 lea     esi, off_742838
.text:004BCB24                 lea     edi, [esp+88h+var_80]
.text:004BCB28                 cld
.text:004BCB29                 movsd
.text:004BCB2A                 movsd
.text:004BCB2B                 lea     esi, off_740DF8
.text:004BCB31                 lea     edi, [esp+88h+var_78]
.text:004BCB35                 cld
.text:004BCB36                 movsd
.text:004BCB37                 movsd
.text:004BCB38                 mov     [esp+88h+var_70], 0FFFFFFFFh

Jeszcze tutaj:

.text:00650751                 lea     esi, [esp+68h+var_44]
.text:00650755                 lea     edi, [esp+68h+var_24]
.text:00650759                 cld
.text:0065075A                 call    sub_475CA0

I w całej masie kodu wszędzie CLD. Co ciekawe nie znalazłem w binarce ani jednej instrukcji STD, która sprawiałaby, że trzeba użyć CLD 😉

2. Undefined behaviour

W assemblerze znajdziemy instrukcje, których znaczenia nie zna nawet taki wymiatacz assemblera jak Rysio z Klanu. Wśród nich znajdziemy tajemniczą instrukcję UD2, lata temu stosowałem ją w metodach antydebug ze względu na to, że deasemblery w popularnych debuggerach nie wiedziały co z nią robić. Obecnie jest udokumentowana jako celowo nieprawidłowa instrukcja do testowania (sprytnie).

Jej zastosowanie w kodzie generowanym w nowoczesnych kompilatorach praktycznie nie istnieje. Na co komuś instrukcja, która niczego nie robi (oprócz linuxiarzy)? W kodzie kompilatora Go znalazła zastosowanie:

.text:004BC84B loc_4BC84B:                             ; CODE XREF: path_filepath_volumeNameLen+1BAj
.text:004BC84B                 call    runtime_panicindex
.text:004BC850                 ud2
.text:004BC852 ; ---------------------------------------------------------------------------
.text:004BC852
.text:004BC852 loc_4BC852:                             ; CODE XREF: path_filepath_volumeNameLen+1C8j
.text:004BC852                                         ; path_filepath_volumeNameLen+1CFj
.text:004BC852                 mov     ebp, 1
.text:004BC857                 jmp     short loc_4BC833
.text:004BC859 ; ---------------------------------------------------------------------------
.text:004BC859
.text:004BC859 loc_4BC859:                             ; CODE XREF: path_filepath_volumeNameLen+198j
.text:004BC859                 call    runtime_panicindex
.text:004BC85E                 ud2
.text:004BC860 ; ---------------------------------------------------------------------------
.text:004BC860
.text:004BC860 loc_4BC860:                             ; CODE XREF: path_filepath_volumeNameLen+184j
.text:004BC860                                         ; path_filepath_volumeNameLen+18Bj
.text:004BC860                 mov     ebp, 1
.text:004BC865                 jmp     short loc_4BC7EF
.text:004BC867 ; ---------------------------------------------------------------------------
.text:004BC867
.text:004BC867 loc_4BC867:                             ; CODE XREF: path_filepath_volumeNameLen+172j
.text:004BC867                 call    runtime_panicindex
.text:004BC86C                 ud2

Jest ona tu zastosowana jako bariera, upewniająca, że po runtime_panicindex nic nie zostanie wykonane. Tak dla picu i marnotrastwa miejsca. To tak jakby po ExitProcess() dać dla bajery int 3.

3. Optymalizacyjne potworki

Są takie instrukcje, których manuale zarówno Intela jak i AMD rekomendują omijać szerokim łukiem. Dlaczego? Bo to archaiczne twory z początków procesorów 386, które przetrwały na liście instrukcji, jednak nikt ich już od tamtych czasów nie optymalizował w silikonie i nikt ich nie używa, oprócz paru szalonych programistów assemblera, którzy optymalizują kod pod względem rozmiaru.

Jedną z takich instrukcji jest xchg, która wymienia zawartość 2 rejestrów, od czasów MS-DOS nie widziałem, żeby jakikolwiek kompilator generował tą instrukcję w kodzie (oprócz ręcznych wstawek w assemblerze). A tutaj mamy tego całą masę:

.text:004BC7C4                 xchg    eax, ebp
.text:004BC7C5                 cmp     al, 0
.text:004BC7C8                 xchg    eax, ebp
.text:004BC7C9                 jz      loc_4BC86E
.text:004BC7CF                 inc     eax
.text:004BC7D0                 cmp     eax, ecx
.text:004BC7D2                 jnb     loc_4BC867
.text:004BC7D8                 lea     ebp, [edx+eax]
.text:004BC7DB                 movzx   ebp, byte ptr [ebp+0]
.text:004BC7DF                 xchg    eax, ebp
.text:004BC7E0                 cmp     al, 5Ch
.text:004BC7E3                 xchg    eax, ebp
.text:004BC7E4                 jz      short loc_4BC860
.text:004BC7E6                 xchg    eax, ebp
.text:004BC7E7                 cmp     al, 2Fh
.text:004BC7EA                 xchg    eax, ebp
.text:004BC7EB                 jz      short loc_4BC860
.text:004BC7ED                 xor     ebp, ebp

Na pohybel  manualom :P, już nawet nie mówię, że ten kod wygląda tak biednie, że mam ochotę dać darowiznę, nawet niewprawiony programista assemblera lepiej by to napisał.

Kolejny:

.text:00473F19                 mov     eax, offset runtime_call16
.text:00473F1E                 jmp     eax

Podpowiem tym razem: jmp runtime_call16 i jesteście 2 bajty do przodu, niczego nie tracąc, a wręcz zyskując, jmp w tym wypadku to relatywny skok, więc wynikowy plik PE EXE będzie jeszcze mniejszy o rozmiar relokacji używany w instrukcji mov eax,adres_w_pamieci.

I znowu:

.text:0066A88E                 and     ebx, 7
.text:0066A891                 cmp     ebx, 0
.text:0066A894                 jz      short loc_66A89F

Każdy lamer zaczynający programować w assemblerze później czy prędzej dowie się, że instrukcje takie jak and, or, xor ustawiają również flagi w zależności od wyniku wykonanej operacji, nie trzeba dodatkowo stosować porównania do sprawdzenia flagi.

Go lubi nadmiarowy kod:

.text:005FEE6C                 xor     ebx, ebx
.text:005FEE6E                 cmp     eax, ebx
.text:005FEE70                 jz      short loc_5FEEA4

Komentarz? 3 instrukcje do sprawdzenia czy rejestr EAX zawiera wartość 0. Na całym świecie kompilatorowym załatwiają to 2 instrukcje test eax,eax lub cmp eax,0 + skok.

4. Na pohybel Windowsowi

Nikt o zdrowych zmysłach nie korzysta w produkcyjnym kodzie ze struktur takich jak np. PEB czy TEB, gdyż wskutek ewolucji systemów operacyjnych, ich pola zawsze mogą się zmienić. Nikt oprócz kompilatora go.exe, który radośnie wykorzystuje go do śledzenia stosu i w razie potrzeby zwiększania jego rozmiaru:

.text:00473E10                 mov     ecx, large fs:14h
.text:00473E17                 mov     ebx, [ecx+4]
.text:00473E1D                 mov     esi, [ebx]

Kto o zdrowych zmysłach i wbrew dokumentacji, która wyraźnie zaznacza, że struktury te mogą się zmienić w kolejnych wersjach systemu operacyjnego decyduje się na korzystanie z nich? Chyba ktoś, kto nigdy swojego oprogramowania nie testował na 2 różnych wersjach systemu Windows, albo nie zakłada, że będzie on musiał działać na innym OS.

5. Mamy swój standard

W świecie Windows istnieje określony sposób wywoływania funkcji WinApi – określany mianem stdcall, który zakłada, że kolejne parametry są wrzucane na stos i następuje wywołanie zadanej funkcji. Seria instrukcji push zakończona instrukcją call. Proste? Za proste! Skomplikujmy to. Oto jak wygląda wywoływanie funkcji WinApi w go.exe:

.text:00458C33                 mov     eax, SetConsoleCtrlHandler
.text:00458C39                 mov     [esp+14h+var_14], eax
.text:00458C3C                 mov     eax, 2
.text:00458C41                 mov     [esp+14h+var_10], eax
.text:00458C45                 mov     eax, offset runtime_ctrlhandler
.text:00458C4A                 mov     [esp+14h+var_C], eax
.text:00458C4E                 mov     eax, 1
.text:00458C53                 mov     [esp+14h+var_8], eax
.text:00458C57                 call    runtime_stdcall

Czyli istnieje sobie wrapper o sweetaśnej nazwie runtime_stdcall(), któremu przekazywana jest funkcja jako wskaźnik, parametry, a wrapper sam wywołuje instrukcję call. Po co? Kto bogatemu zabroni!

Wnioski

Pierwszy jest taki, że jestem cholernie zawiedziony jakością kodu Go, jak widać można mieć górę pieniędzy, a i tak być technologicznie zacofanym do epoki kamienia łupanego.

Wiem, że w dobie nowych procesorów Intel Core i7 mało kto zwraca uwagę na takie detale, jednak nie wiem czy chciałbym aby moje oprogramowanie i algorytmy były kompilowane do takiej formy jaką obecnie oferuje najnowszy kompilator Go. To się po prostu nie dodaje jak mówi Mariusz Max Kolonko…

Na koniec jeszcze dump z programu Hello World w Go:

package main

import "fmt"

func main() {
	fmt.Println("Hello, 世界")
}
.text:00401000 main_main       proc near               ; CODE XREF: main_main+1Aj
.text:00401000                                         ; runtime_main+119p
.text:00401000                                         ; DATA XREF: runtime_sighandler+42o
.text:00401000                                         ; .text:off_4CFD0Co
.text:00401000
.text:00401000 var_40          = dword ptr -40h
.text:00401000 var_3C          = dword ptr -3Ch
.text:00401000 var_38          = dword ptr -38h
.text:00401000 var_1C          = dword ptr -1Ch
.text:00401000 var_18          = dword ptr -18h
.text:00401000 var_14          = byte ptr -14h
.text:00401000 var_C           = dword ptr -0Ch
.text:00401000 var_8           = dword ptr -8
.text:00401000 var_4           = dword ptr -4
.text:00401000
.text:00401000                 mov     ecx, large fs:14h
.text:00401007                 mov     ecx, [ecx+0]
.text:0040100D                 cmp     esp, [ecx]
.text:0040100F                 ja      short loc_40101C
.text:00401011                 xor     edi, edi
.text:00401013                 xor     eax, eax
.text:00401015                 call    runtime_morestack_noctxt
.text:0040101A                 jmp     short main_main
.text:0040101C ; ---------------------------------------------------------------------------
.text:0040101C
.text:0040101C loc_40101C:                             ; CODE XREF: main_main+Fj
.text:0040101C                 sub     esp, 40h
.text:0040101F                 lea     ebx, off_4B1260
.text:00401025                 mov     ebp, [ebx]
.text:00401027                 mov     [esp+40h+var_1C], ebp
.text:0040102B                 mov     ebp, [ebx+4]
.text:0040102E                 mov     [esp+40h+var_18], ebp
.text:00401032                 lea     edi, [esp+40h+var_14]
.text:00401036                 xor     eax, eax
.text:00401038                 stosd
.text:00401039                 stosd
.text:0040103A                 lea     ebx, [esp+40h+var_14]
.text:0040103E                 cmp     ebx, 0
.text:00401041                 jz      short loc_401096
.text:00401043
.text:00401043 loc_401043:                             ; CODE XREF: main_main+98j
.text:00401043                 mov     [esp+40h+var_C], ebx
.text:00401047                 mov     [esp+40h+var_8], 1
.text:0040104F                 mov     [esp+40h+var_4], 1
.text:00401057                 mov     [esp+40h+var_40], offset dword_48CEE0
.text:0040105E                 lea     ebx, [esp+40h+var_1C]
.text:00401062                 mov     [esp+40h+var_3C], ebx
.text:00401066                 call    runtime_convT2E
.text:0040106B                 mov     edx, [esp+40h+var_C]
.text:0040106F                 lea     ebx, [esp+40h+var_38]
.text:00401073                 mov     esi, ebx
.text:00401075                 mov     edi, edx
.text:00401077                 cld
.text:00401078                 movsd
.text:00401079                 movsd
.text:0040107A                 mov     [esp+40h+var_40], edx
.text:0040107D                 mov     ebx, [esp+40h+var_8]
.text:00401081                 mov     [esp+40h+var_3C], ebx
.text:00401085                 mov     ebx, [esp+40h+var_4]
.text:00401089                 mov     [esp+40h+var_38], ebx
.text:0040108D                 call    fmt_Println
.text:00401092                 add     esp, 40h
.text:00401095                 retn
.text:00401096 ; ---------------------------------------------------------------------------
.text:00401096
.text:00401096 loc_401096:                             ; CODE XREF: main_main+41j
.text:00401096                 mov     [ebx], eax
.text:00401098                 jmp     short loc_401043
.text:00401098 main_main       endp

Jeśli i wy doceniacie piękno assemblera i gardzicie lipnym kodem wpiszcie w komentarzach waszą wersję tej funkcji, najlepszy wpis czeka nagrodapierwszy plakat formatu PE (bo już nie mam go gdzie powiesić). Nie spodziewam się rewelacji z waszej strony, ale kto wie 😉