----------------------------------------------------------------------------
witam ponownie =]

win 32bit i asembler. tym razem cos dla crackerow - patch/crack. piekna
sprawa, nieprawdaz? :)
specjalnie dla mailisty 'AsmPl'. strona listy jest pusta, warto wiec chyba
dopchac tam cos. ludzie z 'CrackPl' tez moga znalezc tu cos uzytecznego :]

3majcie sie

sachy/skid row+dinx project+appendix ................. stonecrk@free.net.pl
(amiga scene coder/cracker, but who cares? :)

ps: w wolnej chwili postaram sie troche zoptymalizowac ten programik.
podesle na asmpl, crackpl i na strony WWW

tytul oryginalu: "_masta_'s Tutorial on Win95 ASM Coding Part 1"
autor oryginalu: _masta_
----------------------------------------------------------------------------

                                      -
                             -----=========-----
      -------==================================================----------
------===== _masta_: Win95 i programowanie w asemblerze, cz. 1 =====------
      -------==================================================----------

                             --==WPROWADZENIE==--

                                  -[CZESC!]-

Po wydaniu czesci 0 otrzymalem kilka e-maili mowiacych, ze to dobry pomysl by
dokonac odrodzenia "starej sztuki" pisania w asemblerze na bazie wINDOWS'ow.
Wszyscy wiemy. ze DOS umiera (miewielu (jesli ktokolwiek) z nas jest z tego
zadowolonych), ale niestety nie mamy wplywu na ten fakt.

                        --==CO JEST NAM POTRZEBNE?==--


                1. Edytor tekstowy
                2. TASM 5.0, z kompletem bibliotek, itp.
                3. Win32-API-Reference (Win32.HLP) - rozpiska Win-API

Zakladam, ze macie podstawowa wiedze o asemblerze, chociaz wiekszosc
materialu jest prosta do zrozumienia.


                         --==SENS TEGO PROJEKTU==--

Chcemy zakodowac (czyli napisac program w asemblerze) "ogolnego patchera"
(ang. Generic-Patcher) znanego rowniez jako "Odszukaj-i-Zniszcz" (ang.
"Search-and-Destroy-Patcher"). Jako, ze wielu ludzi moze nie wiedziec, czago
mozna sie spodziewac po takim patcher'ze, wyjasnie tu po krotce o co chodzi.
Tego typu patcher potrafi nie tylko 'zalatac' program w konkretnej wersji, ale
rowniez moze poradzic sobie z przyszlymi wersjami tego samego programu, o ile
wersje nie roznia sie za mocno. Odbywa sie to poprzez poszukiwanie pewnego
szablonu bajtow (i zapisanie nowego w jego miejsce) a nie tylko 'latanie'
konkretnego offsetu. To czyni patch bardziej uniwersalnym.

Trick polega na tym, ze programisci w nowych wersjach rzadko kiedy zmieniaja
swoje zabezpieczenia w nowych wersjach programu. Zwykle zmianiaja sie jedynie
offsety, ktore nalezy 'zalatac' - wzorzec (szablon) binarny kodu
odpowiedzialnego za zabezpieczenie pozostaje zwykle bez zmian.


                             --==DO ROBOTY!==--

Ok, przemyslmy najpierw strukture programu, potem zajmiemy sie wyznaczeniem
ktore funkcje API uzyjemy a na koncu - napiszemy naszego patchera! :)

1. Intro                - Male 'intro', czyli MessageBox i troche napisow :)
2. Otwarcie Pliku       - Ustawinie 'uchwytu' pliku.
                          Jesli plik nie istnieje -> MessageBox i info o bledzie
3. Rozmiar Pliku
4. Rezerwacja pamieci   - Rezerwacja obszaru pamieci o wielkosci rozmiaru
                          pliku
                          Jesli blad -> MessageBox
5. Wczytanie pliku      - Skopiowanie pliku do zarezerwowanej pamieci
6. Szukanie szablonu    - Poszukiwania offsetu bajtow do 'zalatania'
                          Jesli blad -> MessageBox
7. Ustawienie wskaznika -
   pozycji w pliku na
   offset

8. Patch pliku nowymi   - Patchowanie pliku. Sukces -> MessageBox
   bajtami

9. Zamkniecie pliku     - 'Sprzatanie' systemu po programie
   Zwolnienie
   zarezerwowanej
   pamieci
   Koniec programu

                              --==FUNKCJE-API==--

- Wszystkie komunikaty beda prezentowane za pomoca 'MessageBoxA'.

- Do otwarcia pliku uzyjemy funkcji 'CreateFileA', ktora jest co prawda
  bardziej zlozona niz 'OpenFile', ale rowniez jest bardziej elastyczna.

- Do zamykania pliku uzyjemy 'CloseHandle'.

- Rozmiar pliku mozna uzyskac przy pomocy funkcji 'GetFileSize'.

- Do rezerwacji potrzebnej pamieci sluzy 'GlobalAlloc'; zwolnienie pamieci
  nastepuje przy pomocy 'GlobalFree'.

- Wczytanie pliku nastepuje dzieki 'ReadFile' a zapis przy pomocy 'WriteFile'.

- Wskaznik pliku moze byc ustawiony dzieki 'SetFilePointer'.

- Program zakonczymy przy uzyciu 'ExitProcess'.


                          --==POSZUKIWANIE BAJTOW==--


Ta czesc kodu to serce patchera. Przy pomocy tej malej procedurki, plik jest
przeszukiwany by odnalezc nasza sekwencje bajtow, ktora powinna ulec zmianie.
Glowna idee pracy procedurki opisze tutaj, ale calosc wyjasniona jest
dokladnie w komentarzach w kodzie programu.

Oki, najpierw wrzucamy rozmiar pliku do ECX, na potrzeby instrukcji REPNZ.
Pierwszy bajt poszukiwanej sekwencji bajtow pakowany jest do AL a ESI wskazuje
adres buforka z cala sekwencja.

Przy uzyciu 'REPNZ SCASB' wartosc umieszczona w AL jest porownywana z
wartoscia znajdujaca sie w komorce pamieci, ktora wskazywana jest przez EDI
(a EDI inkrementowana jest o 1). REPNZ powtarza SCASB tak dlugo, az ECX=0, lub
porownywane wartosci sa identyczne (flaga Z=1).
Jesli wartosci sa identyczne, to do ECX ladowna jest ilosc bajtow do
s'patch'owania, a EDI jest zmniejszane o 1, bo SCASB zwiekszylo juz licznik i
trzeba to 'zrownowazyc'.

Kolejne 'REPZ CMPSB' powtarza 'CMPSB'(porownanie wartosci pod adresem [ESI] z
wartoscia spod adresu [EDI]) tak dlugo, az ECX=0 lub sekwencje sie roznia.
(sachy: to chyba jasne, ale... jest to upewnienie sie, ze dana sekwencja,
ktora odnalezlismy i chcemy zalatac - jest ta wlasciwa. wyszuliwanie troche
naiwne, ale dziala :)


                        --==PROCEDURA PATCH'OWANIA==--

Pare slow o samej procedurze patch'owania.
Offset jest obliczany przez zwiekszenie ECX (licznik najtow) o 1 i wartosc ta
odejmowana jest od calkowitej wielkosci pliku:

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      (ROZMIAR PLIKU) - (ILOSC BAJTOW DO KONCA PLIKU) = AKTUALNY OFFSET
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Wartosc ta odkladana jest na stos, jak rowniez handler pliku, ktory bedzie
wykorzystany przez funkcje 'SetFilePointer', by ustawic wskaznik lokacji w
pliku na nasz offset.
Po tym na stos odkladane sa: adres zapisywanych bajtow (bwrite), dlugosc
sekwencji, offset dla nowych bajtow i handler pliku. Po tym wywolywana jest
funkcja API: 'WriteFile'.


                             --==KOD ZRODLOWY==--

Moze troche skompilkowany, ale mysle, ze wciaz w miare latwy do skumania =)

;-------------------------------===START===---------------------------
                ; opcje asemblera
.386P
Locals
jumps

.Model Flat ,StdCall
mb_ok                   equ 0
hWnd                    equ 0
FILE_ATTRIBUTE_NORMAL   equ 080h
OPEN_EXISTING           equ 3
GENERIC_READ            equ 80000000h
GENERIC_WRITE           equ 40000000h


; --==deklaracja uzytych funkcji API==--

extrn     ExitProcess      : PROC ;procedura zakonczenia programu
extrn     MessageBoxA      : PROC ;procedura wyswietlania MessageBox'a
extrn     CreateFileA      : PROC ;   " ...  otwarcia pliku
extrn     ReadFile         : PROC ;odczytanie bloku z pliku
extrn     WriteFile        : PROC ;zapis bloku do pliku
extrn     CloseHandle      : PROC ;zamkniecie pliku
extrn     GetFileSize      : PROC ;pobranie rozmiaru pliku
extrn     GlobalAlloc      : PROC ;rezerwacja pamieci
extrn     GlobalFree       : PROC ;zwolnienie zarezerwowanej pamieci
extrn     SetFilePointer   : PROC ;ustawinie polozenia wskaznika lokacji pliku

; --==tutaj zaczynaja sie nasze dane==--

.Data

caption  db "_masta_: Win32 i Asembler, czesc 1",0

                                   ;tytul okienka, zakonczony zerem

text     db "Czesc, witaj ponownie :)",13,10
         db "Ten tutorek nauczy Cie jak kodowac",13,10
         db "w Win32-ASM: Patcher typu 'Search and Destroy'",0

                                   ;tekst powitalny, zakonczony zerem

err_cap  db "BLAD!",0              ;komunikat bledu dla tytulu

openerr  db "Blad otwarcia pliku!",0       ;tekst bledu otwarcia pliku
memerr   db "Blad allokacji pamieci!",0    ;tekst bledu rezerwacji pamieci
byterr   db "Plik znaleziono, ale nie mozna zlokalizowac oryginalenej sekwencji bajtow!",0

                                            ;blad podczas poszukiwania sekwencji do zapatchowania

readycap db "Gotow!",0                           ;tytul okna dla 'zrobione!'

readytxt db "Ok, plik zcrackowany!",0            ;tekst - 'zrobione!'

file     db "make.old",0        ;nazwa pliku ktory chcemy zpatchowac
org_val  db "Xmas'97"           ;wartosci oryginalne - poszukiwana sekwencja bajtow
new_val  db "_masta_"           ;nowe wartosci - wlasciwy patch
len      equ $-new_val          ;dlugosc patcha
                                ;org_val i new_val musza miec taka sama dlugosc!!!

fhandle  dd  ?                  ;zmienna filehandle
fsize    dd  ?                  ;zmienna the filesize
memptr   dd  ?                  ;wskaznik do zarezerwowanej pamieci
bread    dd  ?                  ;ilosc wczytanych bajtow
bwrite   dd  ?                  ;ilosc zapisanych bajtow

;--==tu zaczyna sie nasz wlasciwy kod==--

.Code
Main:
        push mb_ok              ;wartosc 'uType' na stos
        push offset caption     ;wskaznik do tytulu okna na stos
        push offset text        ;wskaznik do tekstu w onie -na stos
        push hWnd               ;Masterhandle 
        call MessageBoxA        ;wywolaj 'MessageBoxA'

        push 0                                  ;dla Win95 zawsze 0
        push FILE_ATTRIBUTE_NORMAL              ;standardowe atributy pliku
        push OPEN_EXISTING                      ;otworz istniejacy plik
        push 0                                  ;brak atrybutow bezpieczenstwa
        push 0                                  ;dezaktywacja Share-Mode
        push GENERIC_READ + GENERIC_WRITE       ;uaktywnij odczyt/zapis
        push offset file                        ;offset do nazwy pliku
        Call CreateFileA                        ;otworz plik!
        mov  fhandle,eax                        ;zachowaj sobie handler pliku
        cmp  eax,0FFFFFFFFh                     ;jesli eax=FFFFFFFF to nastapil blad!
                                                 
        jnz  file_is_here

        push mb_ok
        push offset err_cap
        push offset openerr
        push hWnd
        call MessageBoxA        ; wyswietl komunikat bledu
        jmp  end_               ; skok do zakonczenia programu

file_is_here:                   ;znalezono zadany plik :)

        push 0                  ;0 jesli rozmiar pliku mniejszy niz 4,3GB :)
        push fhandle            ;handler pliku
        Call GetFileSize        ;pobierz rozmiar pliku
        mov  fsize,eax          ;zachowaj go sobie 

        push fsize              ;filesize = rozmiar bufora
        push 0                  ;0=GMEM_FIXED -> stala przestrzen w pamieci
        Call GlobalAlloc        ;zarezerwuj pamiec, tyle co filesize
        mov  memptr,eax         ;zachowaj wskaznik do zarezerwowanej pamieci

        cmp  eax,0              ;jesli eax=0, tzn. ze byly jakies bledy
        jnz  mem_ok

        push mb_ok
        push offset err_cap
        push offset memerr
        push hWnd
        call MessageBoxA
        jmp  end_kill_handle    ;koniec programu (najpierw zamknij plik!)

mem_ok:                         ;skoro mamy juz zarezerwowana pamiec - idziemy dalej :)

        push 0                  ;0 w wiekszosci przypadkow
        push offset bread       ;wskaznik do ilosci bajtow do przeczytania
        push fsize              ;ile bajtow?
                                ;fsize = caly file
        push memptr             ;wskaznik do zarezerwowanego obszaru
        push fhandle            ;filehandle
        Call ReadFile           ;wczytaj plik!

read_ok:

        mov  edi,memptr         ;ustaw EDI jako wskaznik do memory-area
        mov  ecx,fsize          ;ECX (dla repnz) jako rozmiar pliku
        mov  esi,offset org_val ;ESI wskaznik na sekwencje najtow do odszukania
        mov  al, byte ptr [esi] ;laduj AL pierwszym bajtem sekwencji

loop_:
        repnz scasb             ;powtarzaj az ECX=0 lub AL jest rowne
                                ;wartosci bajtu [EDI], EDI jest
                                ;zwiekszane o 1 przy kazdym wykonaniu instrukcji
        cmp  ecx,0              ;jesli ECX=0 - nic nie znaleziono
        jz   not_found


here_is_something:              ;znaleziono poszukiwana sekwencje

        push ecx                
        push edi
        push esi
        dec  edi                ;EDI-1, poniewaz REPNZ SCASB zrobi o jeden krok za duzo (inkrementacja)
        mov  ecx,len            ;ECX = dlugosc patcha
        repz cmpsb              ;powtarzaj az zawartosci pamieci wskazywane przez
                                ;[EDI] i [ESI] sa rozne lub ecx=0
        cmp  ecx,0              ;jesli ecx=0, wtedy 'org_val' jest w pamieci
        jz   patch              ;->skok do procedury patch'ujacej

not_that:                       ;to nie to miejsce (choc poczatek sie zgadzal)

        pop  esi                
        pop  edi
        pop  ecx
        jmp  loop_              ;kontynuuj poszukiwania

patch:                          ;procedura patch!
        pop  esi                ;POP rejestry
        pop  edi
        pop  ecx
        dec  edi                ;EDI-1
        inc  ecx                ;ECX+1
        mov  eax,fsize
        sub  eax,ecx            ;oblicz Offset
        push 0                  ;offset od poczatku pliku
        push 0                  ;0, jesli plik < 4,3GB
        push eax                ;offset
        push fhandle            ;filehandle
        call SetFilePointer     ;ustaw FilePointer

        push 0                  ;zwykle 0
        push offset bwrite      ;ilosc bajtow do zapisania
        push len                ;dlugosc sekwencji bajtow do zapisania
        push offset new_val     ;offset nowych wartosci
        push fhandle            ;filehandle
        Call WriteFile          ;zapisz blok do pliku

        push mb_ok
        push offset readycap
        push offset readytxt
        push hwnd
        call MessageBoxA        ;OK, patch zrobiony!

        jmp  end_kill_all       ;koniec - robimy porzadki

not_found:

        push mb_ok
        push offset err_cap
        push offset byterr
        push hWnd
        call MessageBoxA        ;nie odnaleziono szukanej sekwencji bajtow

end_kill_all:

        push memptr             ;wskaznik do Memoryarea
        call GlobalFree         ;zwolnienie rezerwowanej pamieci

end_kill_handle:

        push fhandle            ;PUSH filehandle
        call CloseHandle        ;CloseHandle


end_:

        CALL    ExitProcess     ;wyjscie z programu
End Main                        ;koniec kodu

;-----------------------------==KONIEC==----------------------------

;-------------------------------START---------------------------make.bat

tasm32 /mx /m3 /z /q tut
tlink32 -x /Tpe /aa /c tut,tut,, import32.lib

;--------------------------------END----------------------------make.bat


                           --==MALA NOTKA==--

        Jak dotad, nie widzialem potrzeby uzywania include'ow.
        Swoja droga include'y pochodzace z TASMa sa niekompletne,
        ALE jesli ktokolwiek z was posiada kompletny zbior plikow
        *.inc, podeslijcie mi je! :)


                             --==KONIEC==--

Ok, mysle, ze tym razem zrobilismy cos pozytecznego, nie tylko zwykly
MessageBox, ale prawdziwe narzedzie cracker'skie.
Kod zrodlowy moze byc dowolnie uzywany, moze sa tam tez rzeczy, ktore
moglibyscie samodzielnie zoptymalizowac, glownie procedure poszukiwania
sekwencji (Hi Fungus ;)). Dla celow edukacyjnych, to co jest wydaje sie byc
wystarczajacym :)

Male wyzwanie:

--> Poszukiwanie mozna prowadzic szukajac od razu 4 pierwszych bajtow.

Ok, mam nadzieje, ze moja skrzynka mailowa zapelni sie wkrotce (KRYTYKA
ROWIEZ) i zobaczymy sie nastepnym razem, w kolejnym tutorku ... =)

Przy okazji, proboje zalozyc kanal IRCowy, dotyczacy tego tematu...

--> #win32asm
(sachy: kanal taki istnieje juz na IRC-Necie i (kanal glowny) na EFNET. sam
_masta_ pojawia sie tam czasami, to fajny gosc, mozecie zawsze go zapytac o
cos konkretnego. na efnecie polecam jeszcze #cracking i #cracking4newbies -
czasami mozna trafic na jakas rzeczowa dyskusje. :)

Mam nadzieje, ze sa ludzie, ktorych te sprawy interesuja, jak rowniez ludzie
posiadajace podobna wiedze i chetni do dzielenia sie nia.


                           --==POZDROWIENIA==--

                                  _masta_

        VucoeT (Translator and Designer(:])), scut (Idea is from your
        DSP), |caligo| (bad news about you :(), fravia+ (best on the
        web), +Aescalapius (nice Bytepatcher) not4you (we Ossis must
        stick together ;)), fungus (something to optimze), Quest,
        Silvio, TheDoctor, everyone on #LAC and #cracking4newbies
        and to every cracker around the world.

                                  sachy/sr
        _masta_ (thx for permission! :), asmpl, crackpl, wszystkich
        sceniarzy C=64/Atari/Amiga/PC oraz wszystkich, ktorzy czytaja
        tego tutorka :)


                           --==WISE WORDS==--
  -------====================--          --====================--------
------======everybody was a lamer, before they become ELITE======-------
  -------====================--          --====================--------
                          -----==========-----
                                   -
