_______________________________________________________________________________________________

REVERSING, AGGIUNTA DI FUNZIONI, MODIFICA DEL CODICE ESISTENTE E CRACKING CLASSICO IN UN
BERSAGLIO DOC DI CASA MICRO$OFT: NOTEPAD.EXE....DESCRIZIONE DETTAGLIATA DELLA CREAZIONE DI
HNOTEPAD.

REVISIONE 1

By -NeuRaL_NoiSE 1999
_______________________________________________________________________________________________


Hiho!

'Mmazza che titolone impegnativo :)))
allora....eccoci qui di nuovo alle prese con mamma Micro$oft...in questo tutorial spieghero' per
filo e per segno il procedimento che ho seguito per modificare qua e la' il caro vecchio
Notepad.exe in modo da aggiungere/rimuovere/modificare qualche funzione, per meglio adattarlo al
progetto che stiamo cercando di portare a termine io, Anub|s e Insanity.

Ho gia' pubblicato un tutorial sulla creazione di Hnotepad, ma chi ci ha dato un'occhiata si
sara' reso conto che il tutorial precedente era improntato solo e unicamente alla compatibilita'
con Windows 95, niente 98, e soprattutto avra' notato che la generazione del codice era
strettamente legata al computer su cui veniva eseguita. Io stesso suggerivo a qualcuno di
lavorarci su, ma alla fine ho reso il tutto trasportabile...e qui vi descrivero' come, con poche
aggiunte/cambiamenti marginali, il vostro codice funzionera' sia sotto 98 che sotto 95.

Un'ulteriore miglioria rispetto al tutorial precedente sta nel fatto che dopo le modifiche che vi
descrivero' in questo tutorial il vostro notepad leggera' files ben piu' grandi dei soliti ffff
bytes (un ringraziamento particolare a GEnius per la sua idea, scusate il gioco di parole, a dir
poco geniale :), fermo restando che potrete solo visualizzarli, non editarli -- ricordate il nag
della memoria insufficiente ? ho ritenuto opportuno lasciarlo, puo' essere comodo avere un nag
che vi avverte se la vostra pagina sta eccedendo i 50 k. Ciononostante potrebbe darsi che alla
prossima edizione ci infilo un'opzione apposta, chi lo sa...hnotepad e' un progetto in continua
evoluzione, ed e' strabiliante il numero di persone che mi chiede di aggiungere funzioni :)

Ecco le sezioni del tutorial:


* PARTE 1 : Premesse


* PARTE 2 : INTRO A HNOTEPAD


* PARTE 3 : CREAZIONE DI HNOTEPAD:

            FASE 1 : IL NUOVO MENU
            FASE 2 : L'ABOUT BOX
            FASE 3 : AGGIUNTA DI MASKS ALLA STRUTTURA OPENFILENAME
            FASE 4 : AGGIUNTA DEL FILE .INI
            FASE 5 : RIMOZIONE DEL LIMITE NELLA GRANDEZZA DEI FILES APERTI


* PARTE 4 : Bugs conosciuti

* PARTE 5 : Saluti


_________________

PARTE 1: Premesse
_________________

Era un tempestoso giorno di bufera, quando mi avventurai per i meandri dell'IRC in cerca di un
canale amico, che trovai essere #crack-it....c'erano i soliti amici di sempre, a partire da
Insanity, fino a Quequero e ad Anub|s....chissa' come io ed Insa (che e' il webmaster di
RingZ3r0) cominciammo a parlare della programmazione in raw html (cosa in cui lui e'
particolarmente capace:), e mi disse che il tool usato per le sue pagine web e' semplicemente il
Notepad di Window$....notai una certa partecipazione anche da parte di Anub|s, che evidentemente
sosteneva le tesi del nostro amico...Ad un certo punto, mi venne un idea....perche' non iniziare
un simpatico progetto ? proposi l'idea agli astanti: creare un bel file .hlp (tipo guida di
Window$) con tutte le basi necessarie a fornire una COMPLETA conoscenza della programmazione in
raw html, senza bisogno di ingombranti editors visuali (WYSIWYG).....ovviamente, unito a tale
file, ci sarebbe stato il dovuto editor di testo, e chi meglio di un Notepad.exe "riveduto e
corretto" sarebbe servito ai nostri scopi ?? Anub|s sembro' entusiasta dell'idea, e Insa
successivamente gli promise che avrebbe messo la sua parte nella scrittura del file di
help...cosicche' i compiti erano gia' stati divisi...a loro la scrittura dell'help, e a me il
reversing e la modifica del notepad....
Ora so benissimo che questo tutorial non ha nessun motivo di essere :), ma ho ritenuto opportuno
scrivere qualcosa, giusto per dimostrare quanto puo' essere (relativamente) semplice modificare,
rimuovere e aggiungere funzioni a un qualsiasi target gia' compilato...e per questo semplice
motivo, eccovi la descrizione completa della creazione di Hnotepad.exe...

NOTA IMPORTANTE: Nonostante in questo tutorial venga descritta la creazione di un Hnotepad
universale (sia per 95 che per 98), io usero' il file Notepad.exe che viene fornito con WINDOWS
95...quindi vi consiglio di procurarvi quello per seguire questo tutorial. Funzionera'
perfettamente anche sotto il vostro Windows 98, garantito.

La premessa finale e' quella solita, consiglio vivamente al lettore una buona conoscenza di base
del linguaggio Assembly per Windows, e del reversing in generale.



TOOLS USATI:
____________

* W32Dasm v8.93
* SoftICE v3.24
* HIEW v6.1
* Borland Resource Workshop (BRW) v4.5
* ProcDump32 v1.3
* API Reference




__________________________

PARTE 2 : INTRO A HNOTEPAD
__________________________

C'e' poco da dire....comunque vi enunciero' in modo sommario cio' che faremo in questo tutorial:

* Aggiungeremo un menu a Notepad, che aprira' il nostro nuovo file .hlp
* Creeremo l'about box (ora come ora non ne esiste uno vero, ma capirete in seguito)
* Faremo in modo che nelle masks (*.bla) dei dialogs "Apri file" e "Salva con nome" compaiano
  anche i "Files HTML" (sia *.htm che *.html), "Files JS", e i "Files VBS".
* Aggiungeremo un comodo file .INI, in cui verra' salvato lo stato del WordWrapping (A capo
  automatico) e il nome dell'ultimo file aperto. Al riavvio, il wrapping verra' riposizionato
  nello stato definito dal file, e l'ultimo file aperto sara' riaperto automaticamente se
  hnotepad viene lanciato senza command line. Inoltre, tale file .INI viene creato
  automaticamente da Hnotepad ad ogni uscita dal programma, quindi non c'e' problema nel
  danneggiarlo o nello scriverci dentro.
* Elimineremo la limitazione nella grandezza del file aperto, il vostro Hnotepad non avra'
  limiti di dimensione del file visualizzato. Per fare cio', non toccheremo la Import Table ma
  utilizzeremo codice di scansione del Kernel (e di qualsiasi altra libreria in memoria), che,
  come vedrete in seguito, e' universale e potra' ritornarvi MOLTO utile quando non avete spazio
  per inserire GetProcAddress tra le API importate.

Ecco tutto....andiamo!!!!



__________________________________

PARTE 3 : LA CREAZIONE DI HNOTEPAD

FASE 1 : IL NUOVO MENU
__________________________________


Allora.....cominciamo con il cercare di capire COSA dovremo fare al nostro target:
dobbiamo AGGIUNGERE un menu a quelli gia' presenti del programma...ma teniamo presente che
nel corso della sessione avremo SICURAMENTE bisogno di aggiungere parti di codice "nostro" al
file...nel caso del menu, dovremo aggiungere quella parte della window procedure che analizza le
id di menu selezionate relativa alla scelta del nostro nuovo menu. Cominciamo con il vedere come
si comporta il nostro target quando deve analizzare le id...potremmo risalire alla window
procedure tramite la API RegisterClass e ottenere l'indirizzo della window procedure, ma saremo
molto pratici, e breakeremo nel punto esatto che ci interessa.
Abbiamo detto che ci interessa aggiungere un menu. Quindi proviamo ad analizzare il
comportamento del programma nel caso in cui viene scelto un menu: l'API da utilizzare e' percio'
WinHelpA (che fa partire il file winhelp.exe sotto \windows, che serve ad aprire qualsiasi file
.HLP in formato guida di windows). In SoftICE, quindi, "BPX WinHelpA".
Poi andiamo in notepad, e selezioniamo, dal menu "?", l'item "Guida in linea".
Sice breakkera' su WinHelpA: F12 e vi ritroverete al caller, che sara' qui:

:00401E51 FF7514                  push [ebp+14]
:00401E54 57                      push edi
:00401E55 FF7508                  push [ebp+08]
:00401E58 E80FF3FFFF              call 0040116C    ; <-- TORNIAMO DA QUESTA CALL
:00401E5D 85C0                    test eax, eax
:00401E5F 0F8582000000            jne 00401EE7
:00401E65 FF7514                  push [ebp+14]
:00401E68 57                      push edi
:00401E69 56                      push esi
:00401E6A FF7508                  push [ebp+08]

* Reference To: USER32.DefWindowProcA, Ord:0078h
                                  |
:00401E6D FF1534744000            Call dword ptr [00407434]
:00401E73 EB74                    jmp 00401EE9


Bene....DefWindowProcA sta a rappresentare la zona di codice della window procedure dove i
messaggi vengono elaborati con le modalita' predefinite (ad es, se non analizziamo un tal
messaggio, questi comunque verra' "catturato" da DefWindowProcA che lo elaborera' di
conseguenza). Cio' significa che comunque il valore del menu item passa per (e viene controllato
da) quella call a 1E58 (come il parametro 2/3, passato in EDI) dalla quale noi stiamo tornando.
Entriamovi:

* Referenced by a CALL at Address:
|:00401E58  
|
:0040116C 55                      push ebp
:0040116D 8BEC                    mov ebp, esp
:0040116F 81EC04010000            sub esp, 00000104
:00401175 33C0                    xor eax, eax
:00401177 56                      push esi
:00401178 57                      push edi

:00401179 BE38614000              mov esi, 00406138
:0040117E 8DBDFCFEFFFF            lea edi, dword ptr [ebp+FFFFFEFC]

:00401184 B940000000              mov ecx, 00000040
:00401189 A4                      movsb
:0040118A 8DBDFDFEFFFF            lea edi, dword ptr [ebp+FFFFFEFD]
:00401190 F3                      repz
:00401191 AB                      stosd
:00401192 66AB                    stosw
:00401194 AA                      stosb
:00401195 0FB7750C                movzx esi, word ptr [ebp+0C] ; <-- L'ID VALUE DEL MENU SCELTO
:00401199 83FE20                  cmp esi, 00000020 ; COMPARA ESI E 20h
:0040119C 8BC6                    mov eax, esi
:0040119E 7F1A                    jg 004011BA       ; SE ESI > 20h, SALTA
:004011A0 0F84C1030000            je 00401567
:004011A6 48                      dec eax
:004011A7 83F81B                  cmp eax, 0000001B
:004011AA 7736                    ja 004011E2
:004011AC 0FB68838174000          movzx ecx, byte ptr [eax+00401738]
:004011B3 FF248DF8164000          jmp dword ptr [4*ecx+004016F8] ; ALTRIMENTI ELABORA DI
                                                                   CONSEGUENZA

Abbiamo quindi l'ID del menu (potete controllarle una per una con il brw, sotto la voce menu) in
esi, e poi un check se esi e' maggiore di 20h. 20h equivale a 32 dec, quindi sappiamo che le id
con valore inferiore a 32 verranno elaborate da un jump variabile da caso a caso, che puntera'
ogni volta al codice adeguato (il jmp a 11B3). Quindi, per non complicarci la vita, il nostro
menu dovra' avere un ID value maggiore di 32, cosicche' potremo elaborarlo susseguentemente al
jg a 119E, che porta qui:

* Referenced by a JUMP at Address:
|:0040119E(C)
|

* Possible Ref to Menu: MenuID_0001, Item: "Taglia   CTRL+X"
                                  |
:004011BA 3D00030000              cmp eax, 00000300 ; 300h (768 dec) = menu item "Taglia"
:004011BF 7C21                    jl 004011E2

* Possible Ref to Menu: MenuID_0001, Item: "Copia   CTRL+C"
                                  |
:004011C1 3D01030000              cmp eax, 00000301
:004011C6 0F8E0F030000            jle 004014DB

* Possible Ref to Menu: MenuID_0001, Item: "Incolla   CTRL+V"
                                  |
:004011CC 3D02030000              cmp eax, 00000302
:004011D1 0F8427030000            je 004014FE
..............
..........



e cosi' via con gli altri checks. Ecco trovato un buon punto per inserire la deviazione al
NOSTRO codice, che elaborera' per primo l'ID value relativo al nuovo menu. Ma prima dobbiamo
crearlo, questo menu! :)
Per fare cio', andate in BRW e, nella sezione menu, sotto il menu "?", aggiungete un nuovo item,
io l'ho chiamato "Anub|s+Insa's Help su HTML". Assegnategli il valore 33 (21h), che servira' a
farlo analizzare da questa procedura e non dal jmp variabile di prima. Qualsiasi valore piu'
alto di 32 andra' bene (purche' non sia gia' legato a qualche altro item).
Ora, nella sezione "KEY" aggiungete "VK_F1", che servira' a far uscire QUESTO menu e NON quello
di notepad quando premiamo il tasto F1. Non dimenticatevi di cancellare la stessa dicitura dal
menu attuale di notepad.
Salvate, e come per magia al prossimo avvio di notepad vi ritroverete questo nuovo menu che come
sapete ha id value 21h.
Adesso, quindi, dobbiamo risolvere il problema dell'aggiunta del nostro codice al file. Come
facciamo a inserirlo ?? Ci sono un paio di cosette da controllare. Se aprite il file con HIEW,
con ProcDump o con chissa' quanti altri programmi, avrete tutte le info che vi servono
all'aggiunta del vostro codice. Ovviamente, come sapete, non possiamo occupare PIU' bytes di
quanti sono effettivamente presenti nella sezione .TEXT, e quindi siamo costretti ad APPENDERE il
nostro codice alla fine del file, per poi ridirezionare i jumps dalla sezione .TEXT del pe alla
sezione in cui abbiamo aggiunto il codice, che ora andremo a vedere qual'e'....solitamente,
l'ultima sezione andra' bene: nel caso di notepad, pero', abbiamo gia' aggiunto un menu, cosa che
ha portato all'aumento in dimensione della sezione .RSRC: questo ingrandimento ha portato allo
spostamento della suddetta sezione alla fine del file. Potete controllare facilmente entrando in
HIEW, premendo F8 (dal modo hex o asm) per visualizzare le info relative all'header e quindi F6
per le sezioni. Quindi, dicevo, il pe ora e' stato modificato da parte del brw, e ora l'ultima
sezione e' la .RSRC. Ma resta il fatto che, "nativamente", il file notepad.exe finiva con la
sezione .RELOC, che e' quella in cui andremo ad aggiungere il nostro codice (almeno all'inizio,
quando finiremo lo spazio disponibile ci serviremo della .RSRC). La prima cosa da fare e'
controllare lo SPAZIO che abbiamo (in termini di bytes) per aggiungere codice: controlliamo la
dimensione virtuale (VirtSize, = 91Eh) e quella fisica (PhysSize, = A00h); la virtsize ci indica
il numero di bytes che non dobbiamo modificare, quelli cioe' che sono gia' usati dal prog, mentre
la physsize ci indica la dimensione "raw" della sezione: una semplice sottrazione A00h-91Eh ci
dara' E2h (cioe' 226 dec.) bytes liberi, in cui potremo aggiungere il nostro codice. Non avremo
bisogno di ingrandire la sezione, perche' abbiamo tutto lo spazio che ci serve; se la dovevate
ingrandire, PRIMA di creare il menu con il brw, avreste dovuto caricare il file con procdump,
editare la sezione, e aumentarne la dimensione fisica. Bene! Resta un problema....come fare a
sapere A QUALE OFFSET scrivere il nostro codice?? Semplice...dobbiamo tenere d'occhio l'offset di
inizio della sezione: sappiamo che e' 5000h, quindi Offset_Iniziale+VirtSize = 5000h+91Eh =
591Eh, il nostro entrypoint per l'aggiunta di codice. Abbiamo pero' anche un'ALTRO problema...CHE
codice dobbiamo aggiungere ?? :))
Prima di tutto, tracciamo uno schema delle operazioni che vogliamo che il nostro nuovo codice
compia. Per far si' che il codice salti dalla sezione .TEXT (dove c'e' la call che elabora le id
values) alla sezione .RELOC (dove c'e' il nostro codice), avremo bisogno necessariamente di
sostituire qualche byte con un jump....prenderemo il CMP a 11BA: dovremo fare quindi in modo di
fare si che quel cmp diventi un jump ad un'altra sezione: questo, una volta, poteva comportare
qualche fastidio: un tempo di solito un cracker che aggiungeva codice proprio ai programmi
utilizzava un compilatore per generare gli opcodes validi, dovendo perdere tempo con sottrazioni
inerenti il RVA della prima e della seconda sezione, ecc....poi, un bel giorno e' arrivato
SoftICE con la sua funzione A (Assemble instruction) :))))))))
Grazie a questa salvezza di tool, possiamo assemblare le istruzioni al runtime, volta per volta,
e ottenere pertanto gli opcodes con i quali successivamente patcheremo fisicamente il target.
Gli opcodes saranno universali per le push, per i cmp ecc., e pertanto per QUESTO tipo di
operazioni possiamo utilizzare HIEW direttamente, ma per i jumps a sezioni diverse avremo bisogno
di SoftICE, poiche' la distanza tra gli offsets del punto di partenza e del punto di arrivo in
due sezioni diverse non sono ottenibili con il patching da HIEW. Si, allora ok per i jumps, ma
per le CALLS come facciamo ?? Le calls alle funzioni API (che noi utilizzeremo) non
sono nient'altro che calls ad un puntatore dword, che punta ad uno specifico offset in una
sezione denominata .IDATA (imported data): ogni API che verra' usata dal programma viene
inserita in quella sezione all'avvio, e per servircene dal codice dovremo usare
CALL [DWORD_PTR_DELLA_API]. Per scoprire quale Dword Pointer chiamare per una determinata API,
utilizzeremo W32Dasm. Ad ogni API chiamata (basta fare una ricerca con il nome della funzione nel
disassembly) corrispondera' una call dword ptr [xyz]: ogni volta che vi serve una tale API
chiamerete da hiew quel dword ptr, niente piu' niente meno.
Tutto cio' e' molto semplice, tuttavia potreste chiedervi una cosa...COME facciamo a sapere, da
SoftICE, a QUALE offset saltare dalla sezione .TEXT per raggiungere il nostro codice ?? La
locazione non la possiamo trovare con W32Dasm perche' la parte disassemblata riguarda soltanto la
sezione .TEXT, non la .RELOC (dove ci sara' il nostro codice)...la soluzione
sta in HIEW: aprite notepad.exe con hiew: una volta in modo HEX o DISASM, premete ALT+F1 per
cambiare tra visualizzazione degli offsets in modo Global o Local. Posizionatevi su LOCAL, e
noterete che, se prima avevate "global", ora verra' mostrato l'offset completo di imagebase, che
sara' tutto cio' di cui abbiamo bisogno per sapere a quale VA (Virtual Address) corrisponde
l'offset a cui vogliamo saltare, e inserire "JMP VA_del_nuovo_codice" da Sice, lasciando a lui il
compito della generazione degli opcodes. SOLO PER I JMPS (jump short, quelli da 2 opcodes per
intenderci) e per quelli non troppo distanti non avremo bisogno di Sice, ma potremo inserire
direttamente l'offset relativo in HIEW, in quanto il jump non esce da un raggio ristretto di
codice, e HIEW e' ancora in grado di generare degli opcodes validi. Percio' d'ora in poi, quando
vedrete degli asterischi (*******) a fianco dell'istruzione, significa che la stessa e' stata
assemblata con softice, e che ho patchato il file con i bytes che questi mi ha restituito.
Un'altra cosa....abbiamo detto che dobbiamo far aprire un nuovo file con questo menu, che sara'
il file di Anub|s e Insanity sulla programmazione in HTML...questo file avra' pure un nome
(qualcosa.hlp), anche se io non lo conosco ancora (ma nemmeno loro due mi sa :))), comunque per
la prova useremo un file fittizio HNOTEPAD.HLP. Allora, dicevo, dobbiamo fare si che il codice
riconosca anche il nome "HNOTEPAD.HLP",
solo che questo nome, ovviamente, non e' presente da nessuna parte....quindi dobbiamo
aggiungerlo alla STRINGTABLE del target. La stringtable altro non e' che il posto dove possiamo
visualizzare tutte le stringhe contenute in un file alle cui risorse abbiamo accesso. Potremmo
anche sostituire qualche stringa vecchia con la nuova "HNOTEPAD.HLP", oppure inserire
direttamente nel codice .exe la nostra stringa (ideale per quando non avete accesso alle risorse,
e cosa che faremo anche nel corso di questo tutorial), ma visto che abbiamo accesso alle risorse
di notepad.exe, la AGGIUNGEREMO alla stringtable, e la tratteremo proprio come una
qualsiasi stringa esistente gia' da prima. Quindi il primo passo e', in BRW, selezionare
l'ULTIMA stringtable (la 48), premere il tasto destro e EDITARLA IN MODO TESTUALE (attenti a non
premere solo edit, o non potrete aggiungere items). Alche', semplicemente aggiungete questa riga
alla fine:

 58, "hnotepad.hlp"

Salvate il file, ed il gioco e' fatto! Adesso abbiamo una stringa nuova di zecca che potremo
utilizzare a nostro piacimento...cominciate ad intravedere l'enormita' delle possibilita' che ci
si parano davanti ?? :D
Allora....a questo punto dovete sapere (se non lo sapevate gia':) che le stringhe si caricano
dalla stringtable con la funzione LoadStringA, la cui sintassi e':

int LoadString(
    INSTANCE hInstance,	// handle del modulo contenente la risorsa della stringa
    UINT uID,	        // ID della stringa (nel nostro caso 58, quindi 3Ah)
    LPTSTR lpBuffer,	// Indirizzo del buffer di ricezione della stringa
    int nBufferMax     // dimensione massima del buffer (n. di chars da prelevare dalla stringa)
                       // se la stringa e' piu' corta non fa niente, ma se e' piu' lunga viene
                       // troncata!
   );


Hmmm....il problema qui e' il buffer....come facciamo a sapere quale buffer utilizzare per
inserirvi la nostra nuova stringa ?? Beh, un po' di esperienza pratica direi...cominciate a
settare breakpoints, e troverete molto presto un buffer adatto alla ricezione. Avete due scelte:
trovare un buffer che viene caricato UNA volta all'inizio e MAI piu' (in tal caso dovrete usare
il loadstring una volta in piu', una specie di PUSH e POP con gli indirizzi per intenderci),
o usare un buffer che viene caricato da un altro LoadStringA quando serve....in tal caso, potete
sfruttarlo quando e quanto volete, tanto alla fine sara' il programma stesso a ripristinare il
suo contenuto originale, all'occorrenza. Quest'ultima e' la scelta piu' comoda, decisamente.
Quindi, ad esempio, prendiamo la message box "Questo documento e' troppo grande per notepad,
aprire wordpad??"...se premete si, noterete un LoadStringA che carica in un buffer la variabile
"wordpad.exe", che serve ad avviare il programma....eccovi il disasm (ricordatevi che i valori
vengono pushati sulla stack in ordine inverso):

:00402D70 6804010000              push 00000104 ; PUSHA N.DI CARATTERI DA PRENDERE DALLA STRINGA
:00402D75 8D85B8FEFFFF            lea eax, dword ptr [ebp+FFFFFEB8] ; EAX DIVENTA=63F8C4h
:00402D7B 50                      push eax ; PUSHA 63F8C4h COME BUFFER DI RICEZIONE DELLA STRING

* Reference To: USER32.LoadStringA, Ord:0168h
                                  |
:00402D7C 8B1DB0734000            mov ebx, dword ptr [004073B0]
:00402D82 837D1001                cmp dword ptr [ebp+10], 00000001 ; (DUMMY)
:00402D86 1BFF                    sbb edi, edi                     ; (DUMMY)

* Possible Reference to String Resource ID=00056: "wordpad.exe"
                                  |
:00402D88 6A38                    push 00000038                ; ID DI "WORDPAD.EXE" NELLA TABLE
:00402D8A FF3570514000            push dword ptr [00405170]    ; HANDLE DEL MODULO CON LA TABLE
:00402D90 FFD3                    call ebx                     ; CHIAMA LOADSTRING


Ottimo! Abbiamo trovato il nostro buffer...segnatevi quell'indirizzo di memoria :
63F8C4h...anche se vi sovrascriveremo la nostra stringa "hnotepad.hlp", il buffer verra'
ripristinato da questo loadstring ogni qual volta si cerchera' di avviare wordpad. Notate che
possiamo, in questo modo, anche impossessarci dell' informazione "invariabile" relativa alla
funzione, ovvero l'handle del modulo contenente la stringtable (il "dword ptr [405170]" pushato
a 2D8A, che contiene il valore 400000h, che sara' il nostro handle). Potremmo pushare
direttamente 400000h all'occorrenza, ma utilizzeremo sempre questo puntatore (ricordatevi che
quando modificate targets gia' compilati, e' sempre piu' sicuro, soprattutto per chi non ha
molta dimistichezza, verificare i dati due volte, e in linea generale usare IL PIU' POSSIBILE i
puntatori e IL MENO POSSIBILE i valori immediati).

Adesso sappiamo come ottenere la stringa "hnotepad.hlp" nel nostro codice, ma ci manca ancora il
dettaglio finale....COME chiamiamo la funzione WinHelpA? Ancora una volta la API reference ci
viene in soccorso:

BOOL WinHelp(

    HWND hWndMain,	// handle della finestra che sta aprendo l'help
    LPCTSTR lpszHelp,	// indirizzo della stringa con la path
    UINT uCommand,	// tipo di help
    DWORD dwData 	// dati extra
   );	

Beh, qui e' semplice....ancora una volta, ci renderemo conto del modo in cui viene chiamata la
funzione esaminando esempi PRATICI della stessa all'interno del codice: quindi, un BPX WinHelpA
ci servira' per analizzare il codice quando premiamo ?/Guida in linea...
ecco a voi:

:0040121C 6A00             push 00000000 ; NIENTE DATI EXTRA
:0040121E A194604000       mov eax, dword ptr [00406094] ; EAX=PUNTATORE A STRINGA "NOTEPAD.HLP"
:00401223 6A0B             push 0000000B ; =HELP_FINDER, SEMPLICEMENTE UN MODO DI CHIAMATA HELP
:00401225 50               push eax      ; PUSHA "NOTEPAD.HLP"
:00401226 FF3500604000     push dword ptr [00406000] ; ECCO IL PUNTATORE ALL'HANDLE DELLA WINDOW

* Reference To: USER32.WinHelpA, Ord:0225h
                                  |
:0040122C FF154C744000     Call dword ptr [0040744C] ; CHIAMA WINHELPA


Fantastico....l'handle (che era la cosa che ci premeva di piu') lo troveremo, a tempo debito,
nel dword ptr [406000]...
Un'ultima parola riguardo a questa funzione: la api reference ci informa che dovremo chiamare la
funzione WinHelpA con il parametro 02 (=HELP_QUIT) quando chiudiamo il programma, ma non dovremo
preoccuparci di questo, perche' il notepad lo fa automaticamente al momento dell'uscita.



Bene! Adesso abbiamo tutte le informazioni che ci servono per buttarci nella mischia e
aggiungere codice al target! Per prima cosa, sostituiremo il CMP a 4011BA con un bel JMP 40891E

era:

:004011BA 3D00030000              cmp eax, 00000300
:004011BF 7C21                    jl 004011E2

e diventa:

:004011BA E95F770000 ************ jmp 0040891E ; -> VERSO IL NUOVO CODICE
:004011BF 90                      nop
:004011C0 90                      nop

Abbiamo pertanto sostituito un CMP e un JL con un JMP e due NOP. I nop servono per riempire i
bytes del jl che non ci serve piu' (potevamo anche tenerli, ma e' una questione di ordine e
chiarezza...troverete piu' facile organizzarvi cosi'), perche' spostiamo l'intero check nella
sezione .RELOC.
Un ultimo avvertimento prima di andare ad inserire il nosro codice. Bisogna tener presente che
quando eseguiamo operazioni nostre la stack e i registri vanno mantenuti intatti, in altre parole
non dobbiamo lasciare traccia del nostro "passaggio"...lo possiamo fare facilmente salvando tutti
i registri all'inizio e ripristinandoli alla fine con PUSHAD e POPAD. Ed ora ecco il codice che
andremo ad aggiungere con HIEW all'offset 591Eh (F3 poi F2 per inserire l'istruzione asm...quando
trovate gli asterischi significa che dovete uscire da F2 e inserire manualmente i bytes, che
leggete dopo la dicitura "OPCODES:", cosi' come sono, perche' sono stati generati da softice,
mentre quando trovate le calls a funzioni API potete leggere l'indirizzo del dword ptr da
chiamare in CALL D,[40xxxx]):

N.B.: IN HIEW, PER SCRIVERE "DWORD PTR [xyz]", USATE "D,[xyz]", per "BYTE PTR" usate "B" ecc.
IN GENERALE, RIFERITEVI ALLE ALTRE ISTRUZIONI PER CAPIRE IN CHE FORMA INSERIRE LE VOSTRE.


591Eh: cmp eax, 21    ; eax contiene l'id dell'item. 21h = Id del nuovo menu "Anub|s+Insa ecc."
       jz 5933        ; se hai clickato li', elaboralo - jump a 2 opcodes, quindi hiew e' ok
       cmp eax,300    ; QUESTE SONO LE DUE RIGHE CHE ABBIAMO SOSTITUITO CON IL JMP NELLA
****** jl 4011E2      ; SEZIONE .TEXT: LE RIPRISTINIAMO QUI!...OPCODES: 0F8CB488FFFF
****** jmp 4011C1     ; continua con gli altri checks, ritornando alla sezione .TEXT
                      ; OPCODES: E98E88FFFF  
5933h: pushad                  ; salva tutti i registri
       push 20                 ; numero max di chars da prendere dalla stringa
       push 63F8C4             ; indirizzo del buffer di ricezione della stringa, vedi sopra
       push 3a                 ; 3Ah=58 dec...ovvero l'id della nostra stringa "hnotepad.hlp"
       push dword ptr [405170] ; handle del modulo contenente la table
       call LoadStringA        ; carica 20 chars della stringa "hnotepad.hlp" in 63F8C4
                               ; CALL D,[4073B0]
       popad                   ; ripristina tutti i registri

                ; a questo punto dobbiamo inserire la chiamata a WinHelpA
      
       push 00                ; niente dati extra
       push 03                ; (=HELP_INDEX, ci fara' uscire l'indice della guida all'apertura)
       push 63f8c4             ; buffer contenente "hnotepad.hlp"
       push dword ptr [406000] ; handle della owner window (vedi sopra)
****** jmp 40122c              ; torna alla sezione .text dove c'e' la call a WinHelpA
                               ; OPCODES: E9CE88FFFF


Fantastico...adesso fate partire il vostro "nuovo" notepad, facendo attenzione a mettere un file
di help di windows con il nome "hnotepad.hlp" nella stessa directory da cui state facendo partire
notepad....clickate sul nuovo menu che vedete sotto "?", et voila'! il gioco e' fatto...abbiamo
aggiunto una funzione nuova di zecca a notepad.exe :)



__________________________________

FASE 2 : L'ABOUT BOX
__________________________________
      
      
Eccoci quindi alla fase due....perche' mettere un nuovo about box ?? semplice...innanzitutto, e'
importante che Insa e Anub|s vengano ringraziati e "creditati" nell'about, e poi un minimo di
soddisfazione personale....:))
Inoltre, scopriremo che non e' tutto oro quello che luccica...credevate che sarebbe bastato
cambiare un paio di stringhe qua e la' per cambiare l'about box ?? Vi sbagliavate...quei lamers
alla Micro$oft hanno inventato una funzione molto pratica, che si trova in shell32.dll...la
funzione di cui parlo e' ShellAboutA. Questa API non fa altro che creare una specie di message
box PREDEFINITA, con la percentuale di risorse rimaste, la memoria ecc. (insomma l'about di
notepad) piu' qualche info addizionale che si puo' specificare al momento. Inutile dirlo, cio'
ci fa una tale rabbia che NON POSSIAMO PASSARCI SOPRA :)
Un bpx ShellAboutA (caricate prima gli exports di shell32.dll, con "File/Load Exports" dal symbol
loader di SoftICE, o inserendola tra gli exports di winice.dat e riavviando) ci indica presto il
punto in cui comincia il codice per l'about box:


:004013D0 6A02                    push 00000002
:004013D2 A170514000              mov eax, dword ptr [00405170]
:004013D7 50                      push eax

* Reference To: USER32.LoadIconA, Ord:015Eh
                                  |
:004013D8 FF1550744000            Call dword ptr [00407450]
:004013DE 50                      push eax
:004013DF 683C614000              push 0040613C
:004013E4 FF3560604000            push dword ptr [00406060]
:004013EA FF3500604000            push dword ptr [00406000]

* Reference To: SHELL32.ShellAboutA, Ord:004Ch
                                  |
:004013F0 FF1580734000            Call dword ptr [00407380]
      

bah, tutta roba inutile....piuttosto, cerchiamo di inserire un po' di codice nostro che faccia
uscire un nuovo about box....

Come nella situazione di prima, dobbiamo renderci conto di quello che stiamo per fare. Una
message box andra' benissimo. Ecco la sintassi per questa semplice funzione API:

int MessageBox(
    HWND hWnd,	        // handle della owner window
    LPCTSTR lpText,	// indirizzo del buffer del TESTO DELLA MESSAGE BOX
    LPCTSTR lpCaption,	// indirizzo del buffer del TITOLO DELLA MESSAGE BOX
    UINT uType 	        // stile della message box
   );

Hmmm....cosa facciamo con quei due buffer ?? il testo che vogliamo inserire e' lunghetto....
potremmo aggiungere altre stringhe alla stringtable e utilizzare altri buffers per fungere da
titolo e testo della msgbox, ma pensate ad una cosa...nella FASE 4 di questo tutorial
rimuoveremo il box "file troppo esteso, avviare wordpad?", visto che elimineremo la limitazione
nella grandezza dei files aperti, e, di conseguenza, potremmo inserire il nostro testo di about
in questa stringa nella table...cosicche' il puntatore a questa stringa (che sta a 4060B0, come
possiamo vedere breakkando sulla message box suddetta) punterebbe adesso al testo del nostro box
di about...quindi tutto cio' che dobbiamo fare e', in BRW, modificare la stringa che ha come id
52....cambiarla da

"File troppo esteso per essere aperto.\nUtilizzare WordPad per leggere il file?"

in

"RingZ3r0's Hnotepad v1.00\nVersione modificata di Micro$oft's Notepad.exe\n\n\n* Reversing del codice e
implementazione nuove funzioni di -NeuRaL_NoiSE\n\n* File di help sulla programmazione in HTML 
di Anub|s e Insanity"

Potete semplicemente fare copy&paste del testo suddetto. I "\n" vanno a capo, come in c.
Resta il problema del titolo. Hmm vediamo un po'....il titolo dell'about box deve essere
qualcosa come "Hnotepad" giusto ?? Beh, adesso date un'occhiata a tutte quelle stringhe in cui
compare "Blocco note" nella stringtable...facciamo una cosa. Modifichiamo tutti i "Blocco note"
che troviamo in "Hnotepad"....adesso facciamo partire il programma e facciamoci un giro in
memoria....una semplice s 0 l ffffff 'Hnotepad' da SoftICE ci indichera' un punto della memoria
dove c'e' la stringa suddetta....soffermiamoci un attimo sulla corrispondenza a 41029F. Noterete
che e' preceduta e seguita da bytes 00....cio' significa che la stringa e' tutta li', e' solo
"Hnotepad"...un titolo perfetto per una msgbox non trovate ?? :)
Potremmo, come sopra, pushare direttamente l'indirizzo 41029F al momento della call a
MessageBoxA...ma troveremo il puntatore a questo buffer, e pusheremo quello, invece...sempre per
gli stessi motivi di prima. Non fatevi spaventare dal suono delle parole, un buffer e'
semplicemente una variabile, e un puntatore.....beh diciamo che e' il "nome" di quella
variabile, giusto per dirla in modo spicciolo. I puntatori sono nella sezione .DATA. Abbiamo
gia' visto un puntatore prima, ricordate? Parlo dell'handle del "modulo contenente la
stringtable" per la funzione LoadStringA....quel puntatore era a 405170....cio' ci basta per
individuare la sezione .DATA...ora tutto sta a trovare un puntatore (il 'nome') al buffer (la
'variabile') 41029f ("Hnotepad")...presto fatto, ecco li' il nostro puntatore: 406060.
allora....cosa altro ci resta ?? hmmm, l'handle della owner window.....da un esempio pratico di
MessageBoxA (quello relativo alla richiesta "caricare wordpad?"), notiamo che ebp+8 contiene
l'hwnd....e questo non cambia, come vedrete se sperimentate un po'....quindi, abbiamo anche
l'ultimo dato che ci serviva....l'hwnd!
Ora non resta che decidere lo stile della message box....direi che, trattandosi di un box di
about, un valore 0 (=MB_OK, cioe' un unico button con su scritto "ok") andra' benissimo come
style....

Allora siamo pronti! Modifichiamo anche quest'altro codice in modo che faccia quello che gli
ordiniamo....il VA a cui appenderemo il nostro codice dell'about box sara' 40895E (offset 595Eh),
quindi dovremo inserire un jump nella sezione .TEXT per saltare al nostro
codice. L'inizio della routine di ShellAboutA andra' bene:

era

:004013D0 6A02                    push 00000002
:004013D2 A170514000              mov eax, dword ptr [00405170]

e diventa

:004013D0 E989750000 ************ jmp 0040895E ; --> VERSO IL NUOVO CODICE


Il codice che segue il nuovo jmp viene modificato completamente, ma non ci interessa visto che
non ci serviva a niente...adesso il programma continuera' nella sezione .RELOC all'offset 595Eh,
con il NOSTRO codice:

       ...

595Eh: pushad                  ; salva tutti i registri
       push 00                 ; 0 = MB_OK
       push dword ptr [406060] ; pusha il puntatore a "Hnotepad" come TITOLO
       push dword ptr [4060B0] ; pusha il puntatore alla nuova stringa "RingZ3r0 ecc" come TESTO
       mov esi, [ebp+8]        ; hWnd della owner window in esi
       push esi                ; pusha l'handle
       call MessageBoxA        ; mostra la message box - CALL D,[407430]
       popad                   ; ripristina tutti i registri
****** jmp 4016eb              ; torna a .text - OPCODES: E96E8DFFFF

       nop                     ; finisce anche questa parte di codice, il nop e' inutile ma se
                               ; state seguendo questo tutorial passo passo inseritelo anche
                               ; voi, per ritrovarvi con gli offsets.
      

Bene! Anche questa e' andata.....provate ora a far uscire l'about box....noterete che mostra la
stringa che gli abbiamo cambiato, con in piu' "Hnotepad" come titolo....ovviamente se ora
provate ad aprire un file piu' grosso di FFFFh bytes vi uscira' un box senza senso, poiche' la
vecchia stringa "File troppo esteso..." non esiste piu'...ma e' solo questione di pazienza,
elimineremo anche quel box.

      
      

______________________________________________________

FASE 3 : AGGIUNTA DI MASKS ALLA STRUTTURA OPENFILENAME
______________________________________________________


Bene....visto che questo sara' un editor per files Html, mi sembra pure ovvio che debba
contenere anche le masks "Files HTML", "Files JS" e "Files VBS" tra quelle
dell'apertura/salvataggio files....(o meglio, lo devo fare altrimenti Anub|s mi
ammazza.....:)))))) sto scherzando ovviamente ;)))

Partiamo dal dialog box "Apri file".
Innanzitutto, dovete sapere che quel dialog box "Apri File" e' un dialog box predefinito, che
viene chiamato con una API specifica, ovvero GetOpenFileNameA, contenuta in Comdlg32.dll...
quindi, per lavorare con questa API, cominciate a caricare gli exports di questa dll.
Un'altra cosa da sapere, e' che comunque le stringhe filtro (masks) vengono specificate, assieme
ad altri parametri (vi rimando alla API reference o ai tutorials di Iczelion per maggiori
dettagli) in una struttura di tipo OPENFILENAME, che viene pushata sulla stack prima della call
a GetOpenFileNameA. Ergo, la struttura conterra' tutte i dati di cui abbiamo bisogno - quindi
anche i filtri. I filtri vengono definiti in questo modo (prendiamo ad es. un filtro per files
di testo): Filtro db "FILES DI TESTO",0,"*.txt",0,0 .....<--- il secondo zero non e' un errore,
ma viene messo se il filtro e' l'ultimo della lista. Altrimenti un solo zero e poi la
descrizione del filtro successivo. Questo e' tutto quello che ci serve. Possiamo definire nella
stringtable una nuova stringa, che andra' alla posizione 59 (3Bh):

 59, "Files HTML|*.htm; *.html|Files JS|*.js|Files VBS|*.vbs"

Non preoccupatevi per ora per quel "|" tra "descrizione" e "filtro", ci serve solo per
identificare un punto in cui piazzeremo uno ZERO BYTE una volta APPESA la stringa alla lista dei
filtri gia' presenti, in modo da far credere al programma che si tratta di varie stringhe diverse
e non di un'unica stringa continua ("descrizione",0,"filtro effettivo",0,ecc.ecc.).
NB: Abbiamo inserito la dicitura "*.htm;*.html" per far capire al programma che dovra' includere
sia i files con estensione .htm che quelli con estensione .html sotto la stessa mask.
In Sice, BPX GetOpenFileNameA e poi scegliete "File/Apri". Sice poppera', premete F12 e il
dialog "Apri file" vi comparira' sullo schermo. Premete ESC e vi ritroverete in Sice, a questo
punto:


:004012E7 6880524000              push 00405280    ; LA STRUTTURA OPENFILENAME VIENE PUSHATA....
:004012EC C7058C52400080514000    mov dword ptr [0040528C], 00405180
:004012F6 C7059052400030524000    mov dword ptr [00405290], 00405230
:00401300 C705B452400004100000    mov dword ptr [004052B4], 00001004
:0040130A 83C003                  add eax, 00000003
:0040130D A3BC524000              mov dword ptr [004052BC], eax

* Reference To: comdlg32.GetOpenFileNameA, Ord:0005h
                                  |
:00401312 E8FA350000              Call 00404911     ; <-- .......ED ECCO LA CALL !


Benissimo, adesso diamo un'occhiata in memoria a 405280. Saliamo un po' con il puntatore e, un
paio di PGUP piu' su, troverete chiare traccie delle stringhe-filtro ("Documenti di testo *.txt"
ecc.). Le stringhe filtro terminano, come potete vedere, all'indirizzo di memoria 4051B0 (con il
secondo BYTE 00, che denota la fine del membro). Quindi, un semplice LoadStringA su questo
indirizzo ci permettera' di APPENDERE la nostra nuova stringa-filtro ai filtri gia' presenti.
il nostro codice, ancora una volta, continuera', giu' nella sezione .RELOC, al VA 40897E
(offset 597Dh). Il jump al nostro codice lo inseriremo al posto della push (all'offset 6E7h),
che ripristineremo in seguito.

era

:004012E7 6880524000              push 00405280
:004012EC C7058C52400080514000    mov dword ptr [0040528C], 00405180

e diventa

:004012E7 E992760000 ************ jmp 0040897E ; --> VERSO IL NUOVO CODICE
:004012EC C7058C52400080514000    mov dword ptr [0040528C], 00405180


Come potete notare, il MOV seguente non cambia, percio' potremo semplicemente tornare qui quando
avremo finito con la modifica e il pushing della struttura.

Adesso tutto cio' che dobbiamo fare e' modificare, allo stesso scopo, il codice relativo al
dialog "Salva con nome". Questo dialog viene chiamato con la API GetSaveFileNameA (sempre in
comdlg32.dll). Non avremo bisogno di grosse modifiche, bastera' far puntare il codice della
push della struttura relativa al dialog "salva con nome" al nostro nuovo pezzo di codice,
esattamente lo stesso procedimento del dialog "Apri file". Percio', con un bpx su
GetSaveFileNameA ci accorgiamo che il codice da modificare e' questo:

era

:0040168A 6880524000              push 00405280 ; pusha struttura
:0040168F E86B320000              Call 004048FF ; Call GetSaveFileNameA

e diventa

:0040168A E9EF720000 ************ jmp 0040897E  ; --> VERSO IL NUOVO CODICE
:0040168F E86B320000              Call 004048FF ; Call GetSaveFileNameA



Come vedete la successiva call a GetSaveFileNameA resta invariata, quindi non abbiamo bisogno
di ulteriori modifiche.

Ma come fara' il nostro codice a differenziare tra un GetSaveFileNameA e un GetOpenFileNameA
(una differenziazione e' necessaria, visto che DOBBIAMO sapere in QUALE PUNTO della sezione
.TEXT tornare successivamente all'esecuzione del nostro codice) ??
Date un'occhiata ai registri, una volta arrivati al nostro codice (o al jump che ci porta
li') : quando abbiamo scelto "apri file", il valore in ESI sara' sempre 0Ah. Se invece abbiamo
scelto "Salva con nome" sara' sempre un altro. La spiegazione e' molto semplice: in BRW, editate
il menu e scegliete File/Apri. Notate l'item ID: e' 10, che in hex e' 0Ah. esi quindi contiene
ancora l'item ID quando raggiungiamo il nostro codice. Pertanto, ci bastera' controllare se
ESI=Ah, e tornare a .TEXT nel punto relativo.


Il nostro codice, quindi, sara' questo:

       ...

597Eh: pushad                   ; salva tutti i registri

       push 50                  ; numero max di chars da prendere dalla stringa

       push 4051b0              ; indirizzo del buffer di ricezione della stringa (appende ai
                                ; filtri gia' esistenti)

       push 3b                  ; 3Bh=59 dec...ovvero l'id della nostra stringa
                                ; "Files HTML|*.htm; *.html|Files JS|*.js|Files VBS|*.vbs"

       push dword ptr [405170]  ; handle del modulo contenente la table

       call LoadStringA         ; carica la stringa "Files HTML|*.htm; *.html ecc." in 63F8C4
                                ; CALL D,[4073B0]
      
       mov byte ptr [4051ba], 0 ; mette uno ZERO BYTE al posto del "|" tra "Files HTML" e
                                ; "*.htm", di modo da rendere effettiva la separazione tra
                                ; descriz. e filtro, e far credere al programma che si tratta di
                                ; due stringhe diverse
                               
       mov byte ptr [4051c8], 0 ; come sopra ma tra "*.html" e "Files JS"
      
       mov byte ptr [4051d1], 0 ; come sopra ma tra "Files JS" e "*.js"

       mov byte ptr [4051d6], 0 ; come sopra ma tra "*.js" e "Files VBS"

       mov byte ptr [4051e0], 0 ; come sopra ma tra "Files VBS" e "*.vbs"
                                      
       cmp esi,0ah              ; ESI=Ah se veniamo da un click su "APRI FILE"

       jnz 59c7                 ; ESI != 0ah??
      
       popad                    ; se no, ripristina tutti i registri...
      
       push 405280              ; ...pusha la struttura modificata...

****** jmp 4012ec               ; E CONTINUA CON LA PARTE DEL GetOpenFileNameA (torna a .TEXT)
                                ; OPCODES: E92589FFFF
  59C7:      

       popad                    ; se invece esi!=0ah, ripristina tutti i registri...
      
       push 405280              ; ...pusha la struttura modificata...

****** jmp 40168f               ; E CONTINUA CON IL GetSaveFileNameA (torna a .TEXT)
                                ; OPCODES: E9BD8CFFFF


Ecco fatto...ora, i vostri dialogs "Apri File" e "Salva con nome" mostreranno 3 masks in piu'.

Potete anche allungare la nuova stringa-filtro, e conseguentemente aggiungere piu' filtri,
sempre tenendo presente che non dovete superare il blocco di memoria allocata per la struttura,
che non dovete sovrascrivere gli altri membri e che dovrete mettere uno 0 byte tra le varie
"descrizione","filtro","descrizione" ecc...OLTRE A *DUE* ZERO BYTES ALLA FINE DELL'ULTIMO FILTRO.




_______________________________

FASE 4 : AGGIUNTA DEL FILE .INI
_______________________________


Allora, eccoci arrivati alla parte forse un po' piu' complessa di questo tutorial. Innanzitutto
diamo un'occhiata a COME apparira' questo file .INI per default:




****
FILE GENERATO AUTOMATICAMENTE DA HNOTEPAD.EXE
UTILIZZARE SOLO 'S' O 'N' ALLA VOCE 'AutoWrap'

-NeuRaL_NoiSE 1999
****

AutoWrap=N
UltimoFile=




Ora possiamo pensare a come vogliamo strutturare il codice relativo a questa funzione.
Naturalmente dovremo scrivere una parte di codice che LEGGE il .INI all'avvio del programma e una
che lo SCRIVE all'uscita. Analizziamo prima la funzione di LETTURA:


* All'avvio di Hnotepad, il codice dovra' cercare un file predefinito C:\Windows\Hnotepad.ini.
  Se tale file non viene trovato, continuare come se nulla fosse.

* Se viene trovato, leggerne i dati contenuti e cercare il primo uguale ('=') in esso contenuto.
  Dopo quest'uguale ci sara' la posizione desiderata del WordWrapping all'avvio. Se il '=' non
  viene trovato, ritornare a .text senza ulteriori checks e procedere in modo predefinito.

* Se viene trovato il primo '=', analizzare la lettera successiva ad esso, e controllare se e'
  'S', 's', 'N' o 'n'. Se la lettera non corrisponde, tornare a .text e procedere in modo
  predefinito.
 
* Se la lettera corrisponde, accodare o meno il messaggio del wordwrapping alla message queue
  del processo, capirete in seguito come fare.

* Controllare se la Command Line == NULL. Se si e' scelto un file per l'apertura, si dovra'
  caricare questo e non l'ultimo file stabilito nel .INI, poiche' ovviamente quest'ultimo ha
  priorita' inferiore rispetto al file scelto da command line.
   
* SOLO se la Command Line == NULL, si cerchera' l'ultimo file aperto da .INI. Partendo dalla
  posizione precedente, cercare il '=' successivo. Dopo questo '=' ci sara' il nome dell'ultimo
  file aperto. Se il '=' non viene trovato, tornare a .text e procedere con l'apertura di un
  nuovo documento (come notepad si sarebbe comportato normalmente, insomma).

* se il '=' viene trovato, controllare che immediatamente dopo di esso ci sia una lettera
  compresa tra 'a' e 'z', oppure tra 'A' e 'Z'. Cio' ci serve per stabilire (a grandi linee) se
  il nome del file e' corretto (da non confondersi, qui non stiamo controllando se il file
  esiste, stiamo solo controllando che non si verifichi il tentativo di apertura di un file, ad
  esempio, 7:\xyz\xyz.txt"). In altre parole controlliamo che la lettera iniziale sia una
  lettera dell'alfabeto, cioe' un identificatore valido per un drive, e non, magari, un numero
  o un simbolo. Se non e' una lettera valida, tornare a .text e procedere in modo predefinito.

* Se abbiamo trovato una lettera valida, procedere cercando la LUNGHEZZA della stringa che
  rappresenta il nome del file da aprire. salvare la posizione iniziale della stringa, e
  avanzare cercando uno spazio (' ', =20h) o un carriage return (=0Dh). In assenza di uno di
  questi, procedere fino alla fine del file. Da precisare una cosa: quando salviamo il nostro
  file di default, dopo il secondo '=' c'e' uno spazio (' '), che se viene ritrovato nel check
  iniziale indica la fine del nome del file. Se invece salviamo il .INI con il nome di un file
  da riaprire successivamente, vi appenderemo un CR (=0Dh), che funzionera' ne' piu' ne' meno
  come lo spazio. Se l'utente ha inserito manualmente il nome dell'ultimo file nel .INI, e non
  ha inserito alla fine uno spazio o un CR, non c'e' problema comunque poiche' avremo un terzo
  check, quello della lunghezza del file: una volta raggiunta la EOF si considerera' finita la
  stringa di definizione dell'ultimo file.

* Una volta trovata la fine della stringa di definizione del file da aprire, copiarla
  direttamente nel buffer che viene utilizzato per l'apertura di un file quando cmdline!=NULL.
  Notepad provvedera' automaticamente a caricare il file perche' noi effettuiamo questa
  operazione PRIMA che il check 'nativo' per la cmdline viene effettuato. In parole povere,
  'bruteforceremo' il nome di un file nel buffer che altrimenti avrebbe contenuto solo zero
  bytes, 'simulando' quindi la scelta di un file da aprire da command line da parte dell'utente.
 
* A questo punto, torneremo a .TEXT per continuare normalmente con il codice.


Ora stabiliremo di quali funzioni API avremo bisogno. Quando nel codice leggerete una call ad una
API, sostituite in HIEW semplicemente call d,[dword_ptr_della_api].

ecco quali sono i ptr che vi serviranno:

LoadStringA:  call d,[4073b0]
_lopen:       call d,[407364]
_lread:       call d,[407368]
PostMessageA: call d,[40745c]
lstrcpyA:     call d,[40733c]
_lclose:      call d,[4072f0]


Adesso dobbiamo tenere presente una cosa. Per aggiungere altro codice, dovremo sfruttare la
sezione .RSRC, poiche' lo spazio rimasto in .RELOC e' ormai alla frutta. Purtroppo c'e' una
limitazione alla nostra voglia di reverserare tutto, e tale limitazione risiede in BRW.
Ovviamente con un po' di semplice intuito la aggireremo, ciononostante mi sembra giusto che voi
sappiate COSA stiamo affrontando qui :)
La limitazione di cui parlo risiede nel fatto che BRW, ad ogni modifica che effettua sulle
risorse, RICREA da zero la sezione .RSRC. Capite di cosa sto parlando ?? Il vostro simpatico
codice aggiuntivo in .RSRC viene completamente cancellato ad una SINGOLA modifica in una
qualsiasi risorsa effettuata con BRW, e del vostro lavoro non restano tracce. Ci sono vari
sistemi per aggirare il problema, e noi ne utilizzeremo due: innanzitutto immetteremo (quasi)
tutte le stringhe di cui abbiamo bisogno nella stringtable PRIMA di cominciare con il nuovo
codice in .RSRC. Poi, quando tutto sara' scritto, ci ritroveremo a dover utilizzare altre
stringhe per la 5 fase di questo tutorial, ma affronteremo il problema quando ci si parera'
davanti, parlarne adesso mi sembra prematuro.
Pertanto, cominciate con l'aggiungere queste due stringhe alla stringtable del nostro target,
capirete in seguito a cosa serve ciascuna di esse:

 60, "c:\\windows\\hnotepad.ini"
 61, "****\nFILE GENERATO AUTOMATICAMENTE DA HNOTEPAD.EXE\nUTILIZZARE SOLO 'S' O 'N' ALLA VOCE
'AutoWrap'\n\n-NeuRaL_NoiSE 1999\n****\n\nAutoWrap=N\nUltimoFile= "



Un altro problema che incontrerete nell'aggiunta del codice, inoltre, sara' nello spazio fisico
che avete per inserire bytes.
Verso la fine di questo spezzone di codice, infatti, starete quasi per finire anche lo spazio in
.RSRC. Pertanto, gia' da ora, editate il PE di notepad.exe con ProcDump, poi clickate su
"sections", e editate la sezione .RSRC. Ingrandite la Raw size e la Virt Size (per sicurezza) da
3000 a 3200 bytes, e salvate i cambiamenti solo al PE header. Questo ci dara' tutto lo spazio di
cui necessiteremo per aggiungere il nostro ingombrante codice :)

Detto cio', credo che possiamo cominciare a cercare il punto da cui far saltare al nostro codice
per il controllo del .INI. Bisogna tener presenti due presupposti in questa ricerca:

1) per informare il programma che vogliamo il wordwrapping, abbiamo bisogno di 'simulare' un
click sull'opzione "A capo automatico". Questo e' molto semplice, lo possiamo fare con
SendMessageA o con PostMessageA. SendM. salta subito alla window procedure e quindi e' poco
comodo per noi, che dobbiamo effettuare le dovute "pulizie" prima di tornare a .TEXT, pena un
triste GPF o un Page Fault. Pertanto, utilizzeremo PostMessageA, che semplicemente AGGIUNGE il
messaggio alla message queue (coda dei messaggi) del thread in esecuzione. Ma un attimo...per
utilizzare PostMessageA abbiamo bisogno di un HWND a cui mandare il messaggio in questione!
Pertanto il primo presupposto e' che il controllo di EDIT (che notepad usa attualmente) deve
essere GIA' STATO CREATO QUANDO SALTIAMO AL NOSTRO CODICE.

2) tuttavia, se aspettiamo troppo, potremmo incappare nell'errore opposto: il thread ha gia'
creato il controllo di edit, ma anche il check per la command line potrebbe essere gia' stato
fatto! Ecco quindi qual'e' il secondo presupposto: IL CHECK PER LA COMMAND LINE *NON* DEVE ESSERE
GIA' STATO FATTO.

Con buona approssimazione, possiamo supporre che il check per la command line avviene DOPO la
creazione del controllo, e questo e' decisamente un punto a nostro favore. Tutto sta a 'cogliere'
quell' "attimo" tra le due operazioni, e inserirvici di forza tutti i nostri checks.

Direi che un buon inizio puo' essere quella message box "File xyz.xxx non trovato" che poppa
quando cerchiamo di aprire un file inesistente. Essa ci indica infatti un potenziale punto in cui
il controllo e' stato creato e il file da aprire e' stato APPENA controllato.
Ecco dove ci porta un BPX MessageBoxA:


* Reference To: USER32.MessageBoxA, Ord:0176h
                                  |
:00402251 FF1530744000            Call dword ptr [00407430]
:00402257 8BE5                    mov esp, ebp
:00402259 5D                      pop ebp
:0040225A C21400                  ret 0014


Hmm, risaliamo per quel ret...


:004028D0 E84FF9FFFF              call 00402224 ; <-- TORNIAMO DA QUESTA CALL
:004028D5 83F806                  cmp eax, 00000006
:004028D8 7545                    jne 0040291F


Ecco il check del tasto che abbiamo premuto. Ma diamo un'occhiata al codice un po' piu' su nel
disassembly, cos'e' quel CreateFileA?? :)


:00402853 803B00                  cmp byte ptr [ebx], 00
:00402856 0F84F4000000            je 00402950
:0040285C 53                      push ebx
:0040285D 68D0524000              push 004052D0
:00402862 E8F6F9FFFF              call 0040225D
:00402867 6A00                    push 00000000
:00402869 6880000000              push 00000080

:0040286E 6A03                    push 00000003

* Reference To: KERNEL32.CreateFileA, Ord:0039h
                                  |
:00402870 8B1D44734000            mov ebx, dword ptr [00407344]



Molto interessante...qui abbiamo proprio il check che ci serviva...il check della command line!
il byte ptr [ebx] contiene 00 se non si e' scelta command line, ma proviamo a mettere un bpx sul
check (a 402853) e ritorniamo al prompt del DOS, quindi scriviamo Notepad FILE_INESISTENTE...
sice poppera' su 402853. Adesso provate a dare "D EBX"...Vi troverete FILE_INESISTENTE pari pari
a come lo avete scritto...esatto! ecco il buffer della command line! :)
ma un attimo...non e' quello il buffer che dovremo utilizzare...notate quella call successiva?
eccola:

:0040285C 53                      push ebx
:0040285D 68D0524000              push 004052D0
:00402862 E8F6F9FFFF              call 0040225D

Se vi tracciate dentro, noterete che la call e' paragonabile ad un lstrcpy, in altre parole COPIA
il nome del file (in d,[ebx]) nel buffer a 4052d0: se nella command line, come nel nostro caso,
avere inserito qualcosa di simile a FILE_INESISTENTE, dopo la call noterete che in 4052d0 e'
presente questo: FILE_INESISTENTE.txt. Lo scopo quindi e' chiaro: il buffer usato per il file da
aprire e' 4052d0 (come potete riscontrare anche dal fatto che viene pushato come parametro per il
CreateFileA successivo). Questo quindi dovra' essere il buffer in cui andremo a scrivere il nome
del nostro file. Ma c'erano due presupposti, ricordate ? Dobbiamo controllare che il controllo
edit sia stato gia' creato a questo punto. Da SoftICE, arrivati a 402853, digitate HWND NOTEPAD
(se il nome del file che state lanciando e' notepad.exe, altrimenti controllate i tasks attivi
con il comando TASK); noterete che il secondo class name e' un controllo di tipo EDIT...quindi e'
gia' stato creato, e anche il primo presupposto viene rispettato!

Allora....tutto cio' che ci serve e' trovare un punto per la deviazione a .RSRC. Quel JE a 402856
fa proprio al nostro caso.


*** NOTA ***

I punti di ritorno sono diversi:
* se non c'e' il file .INI (o c'e' ma presenta errori) e l'utente NON ha scelto files, torneremo
a 402950 (dove salterebbe quel je normalmente).
* se il .INI c'e' ma l'utente ha scelto una command line, torneremo a 40285c con la call di copia
tra i buffers e di aggiunta dell'estensione.
* se il .INI c'e' e l'utente ha lasciato una cmd line vuota, copieremo direttamente il nome
dell'ultimo file aperto dal file .INI al buffer a 4052d0, e torneremo a 402867 con la prima push
del CreateFileA, che si riferisce a tale buffer.

***FINE NOTA***



Il nostro codice cominciera' a 40BEED (@88edh), e quindi la deviazione sara' questa (@1c56h):

era

:00402856 0F84F4000000            je 00402950

e diventa

:00402856 E992960000              jmp 0040BEED
:0040285B 90                      nop


Ora diamo un'occhiata al nostro codice:
Poiche' potrebbe risultare un po' difficile comprenderne la struttura mediante i soli offsets al
posto di piu' comprensibili etichette, ho deciso di utilizzare le labels con a fianco, tra
parentesi, l'offset a cui corrispondono nel file .exe:


Inizio (@88ed):

  pushad ; salva tutti i registri
  push 20 ; # di caratteri da prendere dalla stringa
  push 63f8c4 ; buffer di ricezione
  push 3c ; =60 dec, cioe' l'ID di "c:\\windows\\hnotepad.ini"
  push 400000 ; handle del modulo con la stringtable
  call LoadStringA

  push 0 ; pusha un dummy parameter sulla stack, lo sovrascriveremo con l'handle del file se
         ; questo viene trovato

  push dword ptr [406000] ; salva l'hWnd. Lo trovate sempre qui, basta fare una ricerca tra i   
                          ; dword pointers della sezione .IDATA; non possiamo usare il vecchio
                          ; [ebp+8] che utilizziamo in precedenza, poiche' tale passaggio viene
                          ; effettuato dopo.



; ORA DOBBIAMO APRIRE IL FILE...USEREMO _lopen:
;
; HFILE _lopen(
;
;    LPCSTR lpPathName,	// pointer to name of file to open 
;    int iReadWrite 	// file access mode
;   );	



  push 63f8c4 ; "c:\windows\hnotepad.ini"
  call _lopen
  cmp eax, -1 ; c'e' stato un errore nell'apertura?
  jnz FILE_TROVATO (@8924h)

    pop eax ; scarica il dummy param
    pop eax ; scarica l'hWnd
    popad   ; ripristina tutti i registri
    jmp ERRORE_NEL_FILE (@8a08h)

FILE_TROVATO (@8924h):
 
  mov [esp+4], eax ; file handle al posto del dummy param




; ORA DOBBIAMO LEGGERE I DATI DAL FILE....USEREMO _lread:
;
; UINT _lread(
;
;     HFILE hFile,	// handle to file
;     LPVOID lpBuffer,	// pointer to buffer for read data
;     UINT uBytes 	// length, in bytes, of data buffer
;    );	




  push ff; numero di bytes da leggere; ATTENZIONE - USATE GLI OPCODES 68FF000000, altrimenti
         ; HIEW inserira' 6AFF, che verra' interpretato da Win98 (non dal 95) come PUSH
         ; FFFFFFFF, non PUSH 000000FF
  push 41096f ; buffer "RingZ3r0's Hnotepad.....", ripristinato in CHIUDI (@89E2h)
  push eax ; file handle
  call _lread
 
  mov edi, 41096f ; cio' che abbiamo letto dal file
  mov ecx, eax ; lunghezza effettiva del file in ecx
  mov al, 3d ; '='
  repnz scasb ; cerca AL ('=') nel buffer a cui punta EDI per una lunghezza di ECX bytes
  jnz ERRORE (@895Ah) ; se il '=' non e' stato trovato

; SE IL '=' e' stato trovato, EDI punta al byte SUCCESSIVO al '='.
 
  cmp byte ptr [edi], 53 ; 'S'
  jz AUTOWRAP (@8967h)
  cmp byte ptr [edi], 73 ; 's'
  jz AUTOWRAP (@8967h)
  cmp byte ptr [edi], 4E ; 'N'
  jz NEXTCHECK (@897Bh)
  cmp byte ptr [edi], 6E ; 'n'
  jz NEXTCHECK (@897Bh)
 
ERRORE (@895Ah):

  pop eax ; scarica hWnd
  mov dword ptr [63f8c4], 40c008 ; questo dword ptr sara' la locazione contenente il VA
                                 ; variabile a cui salteremo con la nostra procedura di
                                 ; ritorno "universale", ovvero CHIUDI (@89E2h)
                                 ; 40c008 e' il VA per ERRORE_NEL_FILE (@8a08h)
  jmp CHIUDI (@89E2h) ; = jmps in HIEW

AUTOWRAP (@8967h)

  pop eax ; ripristina hWnd....
  push eax; ...e lo ri-salva
  push ecx; salva i bytes rimanenti per lo scasb del successivo check, poiche' PostMessageA
          ; modifica il registro ECX



; ORA INVIEREMO IL MESSAGGIO RELATIVO AL WRAPPING, USANDO POSTMESSAGEA:
;
; BOOL PostMessage(
;
;     HWND hWnd,	// handle of destination window
;     UINT Msg,  	// message to post
;     WPARAM wParam,	// first message parameter
;     LPARAM lParam 	// second message parameter
;    );
;
; OVVIAMENTE VOGLIAMO SIMULARE UN CLICK SU UN MENU ITEM: IL MESSAGGIO SARA' QUINDI 111h
; (WM_COMMAND), E wParam SARA' 1Bh (=27 DEC, OVVERO LA MENU ID DI "Modifica/A capo automatico")



  push 0 ; lParam
  push 1b ; wParam
  push 111; = WM_COMMAND
  push eax ; l'hWnd
  call PostMessageA
 
  pop ecx ; ripristina i bytes rimanenti per lo scasb del successivo check
 

NEXTCHECK (@897Bh):
 
  cmp byte ptr [ebx], 0 ; check se CMDLINE==NULL
  jz CONTINUA (@898Dh)
  pop eax ; scarca hWnd
  mov dword ptr [63f8c4], 40285c ; altrimenti VA di ritorno = 40285c, vedi "*** NOTA ***"
  jmp CHIUDI (@89E2h) ; = JMPS in HIEW
 
CONTINUA (898Dh):
 
  mov al, 3d ; '='
  repnz scasb ; cerca AL ('=') nel buffer a cui punta EDI per una lunghezza di ECX bytes
              ; ecx ovviamente e' stato ridotto dal precedente repnz scasb del # di bytes
              ; che distano tra l'inizio del file e il primo '='
  jnz ERRORE (@895Ah) ; se il '=' non e' stato trovato




; ADESSO VERIFICHIAMO SE LA LETTERA IMMEDIATAMENTE DOPO L'UGUALE E' COMPRESA TRA 'a' e 'z' o
; tra 'A' e 'Z' ; i valori ascii sono : 'a' = 61  ,   'z' = 7A  ,   'A' = 41   e    'Z' = 5A


 
  cmp byte ptr [edi], 61 ; 'a'
  jge 2_MINUSCOLA (@899Ah)
  jmp MAIUSC (@899Fh) ; JMPS in HIEW
 
2_MINUSCOLA (899Ah):
 
  cmp byte ptr [edi], 7a; 'z'
  jle OK (@89B8h)
 
MAIUSC (@899Ah):
 
  cmp byte ptr [edi], 41 ; 'A'
  jge 2_MAIUSCOLA (@89A6h)
  jmp INVALID (@89AB); = JMPS in HIEW

2_MAIUSCOLA (89A6h):

  cmp byte ptr [edi], 5a ; 'Z'
  jle OK (@89B8h)
 
INVALID (@89ABh):

  pop eax ; scarica l'hWnd
  mov dword ptr [63f8c4], 402950 ; VA di ritorno, vedi "*** NOTA ***"
  jmp CHIUDI (@89E2h) ; = JMPS in HIEW

OK (@89B8h):

  push edi ; salva posizione iniziale della stringa di definzione del file da aprire
 

CERCA_CR_O_SPAZIO (@89B9):

      inc edi
      cmp byte ptr [edi], 20 ; ' '
      jz TROVATO (@89C7h)
      cmp byte ptr [edi], 0D ; = Carriage Return
      jz TROVATO (@89C7h)
      dec ecx ; ecx contiene i bytes tra il secondo '=' e EOF
      jnz CERCA_CR_O_SPAZIO (@89B9)

TROVATO (@89C7):

  mov byte ptr [edi], 0 ; marca in memoria la fine della stringa che definisce il file
  pop edi ; ripristina posizione inziale della stringa (che ora termina con uno 0 byte)
 
  push edi
  push 4052d0
  call lstrcpyA ; copia EDI (inizio nomefile - zero byte) in 4052d0 (buffer per file da aprire)
 
  mov dword ptr [63f8c4], 402867 ; per il jmp di ritorno, vedi "*** NOTA ***"
  pop eax ; scarica hWnd
 

CHIUDI (@89E2h):

  pop eax ; file handle in eax
 


; ORA DOBBIAMO CHIUDERE HNOTEPAD.INI; USEREMO _lclose:
;
; HFILE _lclose(
;
;     HFILE hFile 	// handle to file to close 
;
;    );	

 
 
  push eax ; handle del file
  call _lclose
 
  push 100            --\
  push 41096f           |
  push 34 ; = 52 DEC    | --> LoadStringA di ripristino buffer "RingZ3r0's Hnotepad....."
  push 400000           |
  call LoadStringA    --/ 
 
  popad
  jmp dword ptr [63f8c4] ; il nostro jump variabile di uscita
 

ERRORE_NEL_FILE (@8A08h):

    cmp byte ptr [ebx], 0 ; il check per la CMDLINE, presente anche in .TEXT
*** jnz 40285c ; se CMDLINE!=NULL, OPCODES: 0F854B68FFFF
*** jmp 402950 ; se CMDLINE==NULL, OPCODES: E93A69FFFF
 
 
  NOP
  NOP ; li ho messi a rappresentare la fine di questo spezzone di codice
  NOP
 




Fine.....ora provate ad editare un file C:\windows\hnotepad.ini, inseritevi due uguali, e dopo il
primo scrivete 'S', mentre dopo il secondo il nome di un file sul vostro HD. Notepad usera'
queste impostazioni automaticamente all'avvio.

Adesso, pero', abbiamo risolto solo parte del problema. Analizziamo ora la parte relativa alla
SCRITTURA automatica del file .INI ad ogni uscita dal programma:

* Arrivati all'uscita, il programma dovra' creare il file "C:\windows\hnotepad.ini"; se il file
  esiste, lo tronca a zero e lo crea da capo; se la creazione fallisce, uscire come se niente
  fosse.

* Se invece il file viene creato, caricare in memoria lo "scheletro" del file .INI, e ritrovare
  le informazioni necessarie alla scrittura dei dati in esso, ovvero il nome dell'ultimo file e
  lo stato del WordWrapping al momento dell'uscita.

* Scrivere in memoria tutti i dati relativi al wrapping e all'ultimo file, aggiungendoli al
  punto giusto allo scheletro gia' presente in memoria.

* Scrivere nel file lo scheletro completo dei nuovi dati

* Uscire dal programma


Per fare tutto cio', chiaramente, abbiamo bisogno di 2 cose:

1) sapere DOVE trovare il nome dell'ultimo file aperto prima di chiudere.
2) sapere DOVE possiamo verificare lo stato attuale del WordWrapping.

Bene...per la prima domanda siamo fortunati: il nome dell'ultimo file aperto si trovera' sempre
nello stesso buffer, anche all'uscita: 4052d0.

Per la seconda, invece, abbiamo bisogno di un po' di tracing.

Ricordate la call principale della window procedure ? quella che esaminava i messaggi inviati
alla window ? diamole di nuovo un'occhiata:


* Referenced by a CALL at Address:
|:00401E58  
|
:0040116C 55                      push ebp
:0040116D 8BEC                    mov ebp, esp
:0040116F 81EC04010000            sub esp, 00000104
:00401175 33C0                    xor eax, eax
:00401177 56                      push esi
:00401178 57                      push edi
:00401179 BE38614000              mov esi, 00406138
:0040117E 8DBDFCFEFFFF            lea edi, dword ptr [ebp+FFFFFEFC]
:00401184 B940000000              mov ecx, 00000040
:00401189 A4                      movsb
:0040118A 8DBDFDFEFFFF            lea edi, dword ptr [ebp+FFFFFEFD]
:00401190 F3                      repz
:00401191 AB                      stosd
:00401192 66AB                    stosw
:00401194 AA                      stosb
:00401195 0FB7750C                movzx esi, word ptr [ebp+0C] ; <-- L'ID VALUE DEL MENU SCELTO
:00401199 83FE20                  cmp esi, 00000020 ; COMPARA ESI E 20h
:0040119C 8BC6                    mov eax, esi
:0040119E 7F1A                    jg 004011BA       ; SE ESI > 20h, SALTA
:004011A0 0F84C1030000            je 00401567
:004011A6 48                      dec eax
:004011A7 83F81B                  cmp eax, 0000001B
:004011AA 7736                    ja 004011E2
:004011AC 0FB68838174000          movzx ecx, byte ptr [eax+00401738]
:004011B3 FF248DF8164000          jmp dword ptr [4*ecx+004016F8] ; ALTRIMENTI ELABORA DI
                                                                   CONSEGUENZA
 
Bene, intuibilmente, per tracciare le operazioni che il codice fa quando selezioniamo il
WordWrapping, la scelta migliore e', in sice, "BPX 401199 if esi==1b". Arriveremo al jmp
variabile a 11b3 e questi, a sua volta, ci indirizzera' a 401491:


:00401491 833D1860400001          cmp dword ptr [00406018], 00000001 ; [406018]=0 se wrap=OFF
                                                                     ; [406018]=1 se wrap=ON
:00401498 1BC0                    sbb eax, eax
:0040149A F7D8                    neg eax
:0040149C 50                      push eax
:0040149D E8EC1E0000              call 0040338E
:004014A2 85C0                    test eax, eax
:004014A4 7415                    je 004014BB
:004014A6 833D1860400001          cmp dword ptr [00406018], 00000001
:004014AD 1BC0                    sbb eax, eax
:004014AF F7D8                    neg eax
:004014B1 A318604000              mov dword ptr [00406018], eax ; AGGIORNA IL PTR CON LA NUOVA
                                                                ; POSIZIONE DEL WRAPPING

 
Possiamo dedurre facilmente da cio' che il dword ptr [406018] non e' nient'altro che una flag di
status del WordWrapping. Se e' 0, il wrapping e' OFF, e questa procedura lo ATTIVA, settando il
dword ptr [406018] a 1. Se e' 1, il wrapping e' ON, e la procedura fa l'esatto contrario.
All'uscita, questo ptr e' mantenuto, e ci indica lo stato attuale del wordwrapping, quindi ci
bastera' controllare questo e comportarci di conseguenza.

Intuibilmente, il punto da cui saltare deve essere quando il programma manda WM_DESTROY, cioe' si
e' ad un passo dall'uscita dal processo.
Per rintracciare la zona esatta, ci serviremo di un sistema molto semplice: l'API RegisterClass.
nel Disasm, cercate "registerclass" e vi ritroverete qui:

 
:00402B19 C745F820604000          mov [ebp-08], 00406020
:00402B20 C745D8AD1A4000          mov [ebp-28], 00401AAD
:00402B27 C745F006000000          mov [ebp-10], 00000006
:00402B2E C745D400100000          mov [ebp-2C], 00001000
:00402B35 50                      push eax
:00402B36 897DDC                  mov dword ptr [ebp-24], edi
:00402B39 897DE0                  mov dword ptr [ebp-20], edi

* Reference To: USER32.RegisterClassExA, Ord:0196h
                                  |
:00402B3C FF15CC734000            Call dword ptr [004073CC]
 

Molto semplicemente, date un'occhiata a quelle locazioni che vengono inserite come membri in
ebp-xy, e capirete ben presto che l'inizio della window procedure e' a 401aad:

:00401AAD 55                      push ebp
:00401AAE 8BEC                    mov ebp, esp
:00401AB0 56                      push esi
:00401AB1 57                      push edi
:00401AB2 8B750C                  mov esi, dword ptr [ebp+0C]
:00401AB5 83FE05                  cmp esi, 00000005
:00401AB8 7714                    ja 00401ACE
:00401ABA 0F8406010000            je 00401BC6
:00401AC0 83FE02                  cmp esi, 00000002
:00401AC3 0F84F0000000            je 00401BB9

con un bpx a 401ab5, noterete che ESI contiene il messaggio che viene analizzato correntemente:
quello che ci interessa, e' ovviamente WM_DESTROY (=02h). Bene, come vedete a 401ac3 c'e' un
jump, che verra' preso solo se il messaggio corrente e' WM_DESTROY. Ci bastera' sostituire quel
VA con l'inizio del nostro codice e avremo risolto il problema:

era

:00401AC3 0F84F0000000            je 00401BB9

e diventa

:00401AC3 0F8450A50000            je 0040C019


Le nuove API che ci serviranno sono solo queste due:

_lcreat:     call d,[406370]
_lwrite:     call d,[4072f8]
ExitProcess: call d,[407354]


Il nostro codice, l'avrete capito, comincia al VA 40c019 (@8A19h):


Inizio (@8A19h):

  pushad ; salva tutti i registri
  push 20 ; # di chars da prendere
  push 63f8c4 ; buffer di ricezione
  push 3c ; = 60 dec, "C:\\windows\\hnotepad.ini"
  push 400000 ; handle del modulo con la stringtable
  call LoadStringA



; ORA DOBBIAMO CREARE IL FILE; USIAMO _lcreat
;
; HFILE _lcreat(
;
;     LPCSTR lpPathName,// pointer to name of file to open 
;     int iAttribute 	// file attribute
;    );
;
; L'ATTRIBUTO CHE USEREMO SARA' 0, CIOE' "Normal" (IL FILE PUO' ESSERE SCRITTO E LETTO SENZA
; RESTRIZIONI.
; _lcreat TRONCA AUTOMATICAMENTE A 0 LA DIMENSIONE DEL FILE SE QUESTO ESISTE GIA', O, IN CASO
; CONTRARIO, LO CREA.


  push 0 ; attributo "Normal"
  push 63f8c4 ; "C:\windows\hnotepad.ini"
  call _lcreat

    cmp eax, -1 ; se c'e' stato un errore nella creazione
    jnz CREATO_OK (@8A46h)
    popad ; ripristina tutti i registri
*** jmp 401bb9 ; dove avrebbe continuato se non avessimo inserito la nostra deviazione
               ; OPCODES : E9735BFFFF

CREATO_OK (@8A46h):

  push eax ; salva file handle
 
  push 100 ; # di chars da prendere
  push 41096f ; buffer di ricezione
  push 3d ; = 61 DEC, ovvero l'ID della stringa "****\nFILE GENERATO AUTOMATICAMENTE...."
  push 400000 ; handle del modulo con la stringtable
  call LoadStringA

  cmp dword ptr [406018], 0 ; e' zero se WordWrapping = OFF
  jz CHECK_ULTIMOFILE (@8A6E)
  mov byte ptr [4109f4], 53 ; sostituisce la "N" di default dopo "AutoWrap=" con "S" (053h)
 
CHECK_ULTIMOFILE (@8A6E):

  cmp byte ptr [4052d0], 0 ; e' 0 se non c'e' un ultimo file aperto
  jnz ULTIMOFILE_PRESENTE (@8A80h)
  mov byte ptr [410a02], 0 ; il SECONDO BYTE DOPO IL SECONDO '='...cosi' lasciamo anche uno
                           ; spazio immediatamente dopo il secondo '=', che ci serve nel
                           ; controllo inziale, in apertura programma
  jmp SCRIVI_DATI (@8A90h) ; JMPS in HIEW
 
ULTIMOFILE_PRESENTE (@8A6E):

  push 4052d0 ; nome dell'ultimo file
  push 400a01 ; buffer che inizia subito dopo il secondo '=' nello scheletro gia' in memoria
  call lstrcpyA
 
SCRIVI_DATI (@8A90h):

  pop eax ; ripristina file handle in eax...
  push eax; ...e lo ri-salva



; ORA SCRIVEREMO LO SCHELETRO NEL FILE: USEREMO _lwrite, MA C'E' UN PICCOLO PROBLEMA. SUL MIO
; PC, PER CAUSE ANCORA SCONOSCIUTE, NON POSSO SALVARE PIU' DI 7Fh BYTES ALLA VOLTA, E POICHE' IL
; TESTO CHE DOBBIAMO SALVARE E' LUNGO 92h BYTES, USEREMO _lwrite 2 VOLTE, LA PRIMA CON I PRIMI
; 7Fh BYTES, LA SECONDA CON I RESTANTI 13h.
;
; UINT _lwrite(
;
;     HFILE hFile,	// handle to file
;     LPCSTR lpBuffer,	// pointer to buffer for data to be written 
;     UINT uBytes 	// number of bytes to write
;    );



  push 7f ; i primi 7fh bytes dello scheletro
  push 41096f ; inizio del buffer con lo scheletro
  push eax ; file handle
  call _lwrite
 
  pop eax ; ripristina file handle in eax...
  push eax; ...e lo ri-salva
 
  push 13 ; i restanti 13h bytes dello scheletro
  push 4109ee ; locazione dove cominciano i restanti 13 bytes dello scheletro
  push eax ; file handle
  call _lwrite
 

; ORA NON CI RESTA CHE CONTROLLARE LA LUNGHEZZA DEL NOME DEL FILE, PUSHARE QUEI BYTES E
; APPENDERE TALE NOME AL NOSTRO .INI


  xor eax, eax ; azzera contatore per la lunghezza del nome del file
  mov edi, 410a01 ; dove c'e' la prima lettera del nome file
 
CHECK_LUNGHEZZA_NOME_ULTIMOFILE (@8AB7h):

  cmp byte ptr [edi], 0
  jz FINECHECK (@8AC0h)
  inc edi
  inc eax
  jmp CHECK_LUNGHEZZA_NOME_ULTIMOFILE (@8AB7h); JMPS in HIEW
 
FINECHECK (@8AC0h):

  mov byte ptr [edi], 0d ; appende un CR (carriage return) alla fine della stringa
  inc eax
  pop edi ; file handle in edi
  push eax ; # di bytes da scrivere
  push 410a01 ; dove comincia il nome del file
  push edi ; file handle
  call _lwrite
 
  push edi ; file handle
  call _lclose
 
  popad ; ripristina tutti i registri
 
  call ExitProcess ; esce dal programma
 
  NOP
  NOP ; fine del codice di questa sezione
  NOP


Ci sarebbe qualcosa da spiegare qui. Come mai ho inserito una call diretta a ExitProcess senza
tornare a .TEXT, e conseguentemente senza mandare il PostQuitMessage per un'uscita piu' "pulita"
? Il problema e' molto semplice...se provate a uscire sotto determinate condizioni (nel nostro
caso in un qualsiasi altro modo che non scegliendo File/Esci) otterrete un fault. Il motivo di
cio' e' ancora oscuro per me, ma gradirei se qualcuno mi fornisse qualche info in piu'. Tuttavia,
nel nostro caso l'ExitProcess non causa problemi, ma e' soltanto un modo piu' "rapido" di
terminare il processo.
Elaborare WM_CLOSE invece di WM_DESTROY non da' risultati molto migliori.

Ogni volta che uscirete e riaprirete, l'ultimo file e il WordWrapping verranno ripristinati.


 

______________________________________________________________ 

FASE 5 : RIMOZIONE DEL LIMITE NELLA GRANDEZZA DEI FILES APERTI
______________________________________________________________


Eccoci quindi arrivati alla quinta ed ultima fase della creazione di Hnotepad. La rimozione del
fastidioso limite nella grandezza dei files aperti in Notepad.
Cio' che faremo e' molto semplice: cambieremo il controllo di tipo Edit (che notepad usa) in un
controllo di tipo RichEdit, tipo quello usato da WordPad.
Per altre informazioni su questo argomento, vi rimando alla API reference.

La cosa si presenterebbe molto semplice, se non fosse per un solo piccolo dettaglio: per
utilizzare il controllo RichEdit, abbiamo bisogno della libreria Riched32.dll. E per caricare la
libreria Riched32.dll abbiamo bisogno della funzione LoadLibraryA. Purtroppo per noi, pero',
notepad non include questa funzione tra quelle importate (potete controllare con W32Dasm, oppure
con hiew dando un'occhiata alla sezione .IDATA)...cio' non e' un grosso problema, potremmo
importare GetProcAddress al posto di una funzione qualsiasi (GetTimeFormatA ad esempio) e
utilizzare tale API per ritrovare l'indirizzo di LoadLibraryA dinamicamente ogni volta, al
runtime. Tuttavia utilizzeremo qui un sistema che non tocca minimamente la import table, e cioe'
un codice scritto da qualcuno (il programmatore di un virus, a quanto mi diceva GEnius) che
esegue una operazione pari a quella di GetProcAddress: la scansione della memoria inerente la dll
necessaria (nel nostro caso Kernel32.dll) e il recupero dell'indirizzo della funzione necessaria
(nel nostro caso LoadLibraryA). Tuttavia non mi soffermero' sulla descrizione dei dettagli della
modalita' di funzionamento del codice, il cui codice sorgente lo potete trovare accluso alla
versione finale di Hnotepad.exe, ma mi limitero' a darvi la versione gia' compilata, che potete
semplicemente COPIARE e INCOLLARE in qualsiasi punto del vostro codice, per poi chiamare (con una
call) l'indirizzo inziale di tale codice; funzionera' sempre e comunque poiche' gli indirizzi
sono tutti relativi a questo breve pezzo di codice. La funzione accetta due parametri, ovvero
l'HANDLE della libreria che include la funzione (kernel32.dll) e il NOME della funzione ricercata
(LoadLibraryA). Ma intanto ecco il codice in formato HEX:

`�

Copiate dal byte 60 ("`") al byte 00 prima del primo asterisco nella lunga serie finale.
Potete quindi usare un programma come UltraEdit32 o Hex Workshop per incollare il codice al
vostro programma .exe.

Ma vediamo come funzionera' questo sistema nel NOSTRO codice: eccoci di fronte al piccolo
problema di cui parlavo prima: avremo bisogno di alcune variabili (3 per la precisione), tuttavia
non possiamo sfruttare la stringtable per i nostri scopi: infatti, una semplice aggiunta
distruggerebbe tutto il nostro lavoro. Come fare quindi ? Semplice: scriveremo le nostre
variabili DIRETTAMENTE nel file fisico, in un punto (alla fine) che non ci serve. Ma procediamo
per gradi.

Innanzitutto dobbiamo caricare la libreria riched32.dll per poi, al momento giusto, cambiare
"EDIT" in "RICHEDIT" e ottenere un edit control che supporta i file grandi.

Quindi, cominciamo a tracciare dall'inizio:

:00401000 55                      push ebp
:00401001 8BEC                    mov ebp, esp
:00401003 83EC44                  sub esp, 00000044
:00401006 56                      push esi

* Reference To: KERNEL32.GetCommandLineA, Ord:00BCh
                                  |
:00401007 FF1548734000            Call dword ptr [00407348]
:0040100D 8BF0                    mov esi, eax
:0040100F 8A00                    mov al, byte ptr [eax]
:00401011 3C22                    cmp al, 22
:00401013 7513                    jne 00401028


La nostra deviazione sara' alla riga appena dopo la call a GetCommandLineA, ovvero il
"mov esi, eax" a 100d. Il nostro codice, inclusi i NOPs precedenti, comincera' a 40c0e3 (@8AE3).
Quindi ecco il cambiamento che effettueremo:

era

:0040100D 8BF0                    mov esi, eax
:0040100F 8A00                    mov al, byte ptr [eax]
:00401011 3C22                    cmp al, 22
:00401013 7513                    jne 00401028

e diventa

:0040100D E9D1B00000              jmp 0040C0E3
:00401012 90                      nop
:00401013 7513                    jne 00401028


Dovremo pertanto ricordarci di ripristinare le 3 istruzioni che abbiamo sovrascritto prima di
ritornare con il jne a 401013.

Ora abbiamo il problema delle variabili. Lasciamo un po' di spazio libero nella sezione .RSRC, e
andiamo a scrivervi le variabili che ci servono. Contate che dovremo inserire il codice di
scansione e il nostro codice, quindi dato un buon margine, cambiate in modo HEX in HIEW e
posizionatevi all'offset 8BA0h, alche' editate gli ascii e scrivete :

LoadLibraryA

Questa sara' la nostra prima variabile. Poi, posizionatevi all'offset 8BB0h e scrivete :

Riched32.dll

Ecco la seconda variabile. Avremo bisogno anche di Kernel32.dll, ma come potete immaginare questa
"variabile" e' gia' presente nel nostro codice: una ricerca da HIEW ci fara' arrivare alla
sezione .IDATA, dove tra i nomi delle librerie importate troverete anche Kernel32.dll. Il VA e'
4076E0. Tracciando un rapido schema riassuntivo, quindi, ecco i VA che ci interessano:

LoadLibraryA = 40C1A0 (@8BA0h)
Riched32.dll = 40C1B0 (@8BB0h)
KERNEL32.DLL = 4076E0 (@48E0h)


Inoltre, useremo una nuova API, che e'

GetModuleHandleA = call d,[40735c]

essa ci fornira' l'HANDLE della libreria KERNEL32.DLL, la quale scansioneremo per la ricerca
della nostra API LoadLibraryA.

Ed ecco il nostro codice di caricamento della libreria:


Inizio (@8AE3):

  pushad ; salva tutti i registri
 
  push 4076e0 ; "KERNEL32.DLL"
  call GetModuleHandleA ; rileva l'handle della libreria Kernel32.dll 
 
  push 40c1a0 ; "LoadLibraryA"
  push eax ; handle di KERNEL32.DLL
  call CODICE_DI_SCANSIONE (@8B0D); LA CALL AL CODICE DI SCANSIONE : OPCODES E813000000

; ATTENZIONE: IL CODICE DI SCANSIONE DA' COME RISULTATO L'INDIRIZZO DELL'API DESIDERATA, MA
; QUESTO NON VIENE RITORNATO IN EAX, BENSI' IN *ECX*

  push 40c1b0 ; "Riched32.dll"
  call ecx    ; CHIAMA IL LoadLibraryA!
 
  popad ; ripristina tutti i registri
 
  mov esi, eax  --\
  mov al, [eax]   |____ Ripristiniamo qui il codice sostituito in .TEXT con il JMP
  cmp al, 22      |     OPCODES PER IL JMP : E9064FFFFF
  jmp 401013    --/
 
CODICE_DI_SCANSIONE (@8B0D):

  ; QUI DOVETE SEMPLICEMENTE INCOLLARE IL CODICE CHE HO FORNITO PRIMA, PER UNA DESCRIZIONE
  ; DETTAGLIATA DELLE SUE FUNZIONI VI RIMANDO AI COMMENTI, DA PARTE DELL'AUTORE, NEL SORGENTE.


Benissimo, adesso il nostro codice e' pronto. Resta solo un problema...dobbiamo creare il
controllo in modo RichEdit, non Edit...altrimenti tutte queste modifiche sono inutili. Per creare
il controllo, dobbiamo trovare la call a CreateWindowExA (che notepad usa come variante di
CreateWindow) che pushi, tra i parametri precedenti, anche una stringa "Edit", e cambiare quella
stringa "Edit" in "RichEdit". Innanzitutto, CREIAMO questa variabile. Sempre in modo HEX, da HIEW
editate gli ascii all'offset 8BC0, e scrivete

RichEdit

Il VA per questa nuova variabile e' quindi 40C1C0.

Per trovare la call che ci interessa, in sice BPX CreateWindowExA e fate partire notepad.
Arriverete a questo punto:

:00402785 53                      push ebx
:00402786 A100604000              mov eax, dword ptr [00406000]
:0040278B 56                      push esi
:0040278C 6A0F                    push 0000000F
:0040278E 50                      push eax
:0040278F 6890010000              push 00000190
:00402794 6858020000              push 00000258
:00402799 53                      push ebx
:0040279A 53                      push ebx
:0040279B 6804013050              push 50300104
:004027A0 688C614000              push 0040618C

* Possible StringData Ref from Data Obj ->"Edit"
                                  |
:004027A5 6890614000              push 00406190
:004027AA 6800020000              push 00000200
:004027AF FFD7                    call edi ; CHIAMA CREATEWINDOWEXA

Molto bene...e' scontato quale sara' il cambiamento:

era

:004027A5 6890614000              push 00406190 ; "Edit"

e diventa

:004027A5 68C0C14000              push 0040C1C0 ; "RichEdit"


Adesso rimangono altri DUE dettagli finali, e poi potremo finalmente dichiarare concluso questo
lunghissimo tutorial :)

1) Se provate ad aprire i file grossi, vi uscira' ancora la message box che vi chiede se avviare
WordPad: bpx MessageBoxA, e risalirete al check della grandezza, che e'

:00402E8B 81FEFFFF0000            cmp esi, 0000FFFF
:00402E91 0F8FEC010000            jg 00403083

Semplicemente NOPPATE quel jg e il problema non si ripresentera'.


2) Se aprite un file grosso e provate a inserire (o avete inserito) il WordWrapping, un
fastidioso nag che dice qualcosa tipo "File troppo grande per eseguire A capo automatico" vi
poppera', per eliminarlo sempre BPX MessageBoxA e risalirete qui:

:004014A2 85C0                    test eax, eax
:004014A4 7415                    je 004014BB ; EVIL JUMP
:004014A6 833D1860400001          cmp dword ptr [00406018], 00000001

Evitando quel jump, eviteremo anche il NAG. Percio' NOPpatelo.

E con questo abbiamo FINITO! :)



_________________________

PARTE 4 : Bugs conosciuti
_________________________


Innanzitutto, devo premettere che sto gia' lavorandoci su per cercare di risolverli, quindi con
le prossime versioni di Hnotepad spero di non ritrovarli piu'...comunque, ora come ora i bugs che
conosco sono due:

* QUANDO SI CHIUDE UN FILE, SI OTTIENE SEMPRE IL DIALOG "IL FILE E' CAMBIATO, SALVARLO?" ANCHE
  SE NON SI E' MODIFICATO IL TESTO DI UNA VIRGOLA. IL PIU' FASTIDIOSO, DECISAMENTE, E CREDO SIA
  LEGATO AL CONTROLLO RICHEDIT.

* A VOLTE, L'APERTURA DI UN FILE GROSSO QUANDO SI E' GIA' *DENTRO* HNOTEPAD, PORTA ALL'USCITA
  DEL VECCHIO NAG "APRI WORDPAD?", PUR LAVORANDO CON IL RICHEDIT E NON CON L'EDIT...CREDO CHE
  AL 99% SI TRATTI DI UN SECONDO CHECK SULLA LUNGHEZZA DEL FILE. CI STO LAVORANDO :)

Se per caso vi capitasse di riscontrare qualche altro bug, per piacere comunicatemelo a
neural_noise@hotmail.com . Se possibile indicatemi anche il sistema operativo sotto cui state
facendo girare Hnotepad.



________________

PARTE 5 : Saluti
________________


Eccoci finalmente giunti al termine...questo tute e' dedicato a (nessun ordine particolare):

GEnius, per avere contribuito enormemente con il suo supporto e le sue idee sul RichEdit, con il
suo preziosissimo aiuto di beta tester, nonche' per avermi fornito il codice di scansione della
memoria. Senza di lui tutto cio' non sarebbe stato possibile, quindi GRAZIE GENIO!! :D

Kill3xx, per essere stato un paziente beta tester e un buon amico...ah, ottimo lavoro con il
PeSentry killo! ;)

d4eMoN (aka Patrizia ;P), per il suo aiuto e la sua disponibilita'...grazie MOSTRO :)

{Suby}, per la sua amicizia e il suo aiuto come beta tester. Won't forget that man :)

BrahzVooZ, per essere la persona piu' studiosa che conosca...TORNA AL CRACKING FRATELLO! :)

Anub|s, per aver accolto con entusiasmo il progetto Hnotepad

Insanity, per essere il web/botmaster piu' simpatico della storia :)

+MaLaTTiA, per essere il moderatore della mailing list piu' inattiva dell'universo e perche'
sara' cosi' buono da darmi qualche info sulla steganografia un giorno o l'altro...THX MALA! ;)

Fravia+, Il grande, il miglior reverser attuale a mio modesto parere.

+ORC, la leggenda, il mito. Un grande. Il PIU' grande :)

Guybrush, Quequero, N6U5, Furb3t, ^courier, guiz, Tin, Carpathia, Zen, e tutti gli altri amici da
#crack-it, #cracking4newbies e RingZ3r0.


a presto,

-NeuRaL_NoiSE 1999