Windows 98 Shell32.dll Reversing | ||
Data |
by Spider |
|
06/03/2002 |
Published by Quequero | |
La
CPU fa quello che vuole il software. |
Qualche mio eventuale commento sul tutorial :))) |
Il software, quello che vogliamo noi. |
.... |
|
.... |
Difficoltà |
( )NewBies ( )Intermedio (X)Avanzato ( )Master |
Vi è mai capitato di dover rinominare
l'estensione di un file avendo il "Mostra estensioni" disattivato?
Beh, a me sì, e non è una cosa piacevole dover visualizzare le estensioni
soltanto per rinominare un file. In questo tutorial vedremo come è possibile
ovviare a questo inconveniente modificando il codice di windows per aggiungere
una funzionalità :-)
Scaricate qui l'allegato.
Introduzione |
Tools usati |
URL o FTP del programma |
Notizie sul programma |
Essay |
1041 DIALOG 0, 0, 227, 204 STYLE DS_SETFONT | DS_MODALFRAME | DS_NOIDLEMSG | WS_POPUP | WS_CAPTION CAPTION "Generale" LANGUAGE LANG_ITALIAN, SUBLANG_ITALIAN FONT 8, "MS Shell Dlg" { CONTROL "", 13057, STATIC, SS_ICON | WS_CHILD | WS_VISIBLE, 7, 3, 18, 20 CONTROL "", 102, EDIT, ES_LEFT | ES_AUTOHSCROLL | ES_READONLY | WS_CHILD | WS_VISIBLE, 35, 7, 132, 14 CONTROL "", 13095, STATIC, SS_ETCHEDHORZ | WS_CHILD | WS_VISIBLE, 7, 29, 213, 1 CONTROL "Tipo:", 13080, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 6, 35, 32, 9 CONTROL "", 13059, EDIT, ES_LEFT | ES_AUTOHSCROLL | ES_READONLY | WS_CHILD | WS_VISIBLE, 51, 35, 172, 14 CONTROL "Percorso:", 13089, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 6, 53, 40, 9 CONTROL "", 13065, EDIT, ES_LEFT | ES_AUTOHSCROLL | ES_READONLY | WS_CHILD | WS_VISIBLE, 51, 53, 172, 14 CONTROL "Dimensione:", 13081, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 6, 70, 41, 9 CONTROL "", 13064, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 51, 70, 172, 10 CONTROL "", 13096, STATIC, SS_ETCHEDHORZ | WS_CHILD | WS_VISIBLE, 7, 84, 213, 1 CONTROL "Nome MS-DOS:", 13090, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 6, 90, 55, 9 CONTROL "", 13058, STATIC, SS_LEFT | SS_NOPREFIX | WS_CHILD | WS_VISIBLE | WS_GROUP, 68, 90, 152, 10 CONTROL "Data creazione:", 13092, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 6, 105, 51, 9 CONTROL "(sconosciuta)", 13072, EDIT, ES_LEFT | ES_AUTOHSCROLL | ES_READONLY | WS_CHILD | WS_VISIBLE, 68, 105, 152, 14 CONTROL "Ultima modifica:", 13093, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 6, 118, 50, 9 CONTROL "(sconosciuta)", 13073, EDIT, ES_LEFT | ES_AUTOHSCROLL | ES_READONLY | WS_CHILD | WS_VISIBLE, 68, 118, 152, 14 CONTROL "Ultimo accesso:", 13094, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 6, 131, 50, 9 CONTROL "(sconosciuto)", 13074, EDIT, ES_LEFT | ES_AUTOHSCROLL | ES_READONLY | WS_CHILD | WS_VISIBLE, 68, 131, 152, 14 CONTROL "", 13097, STATIC, SS_ETCHEDHORZ | WS_CHILD | WS_VISIBLE, 7, 149, 213, 1 CONTROL "&Sola lettura", 13075, BUTTON, BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 68, 157, 58, 10 CONTROL "&Nascosto", 13076, BUTTON, BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 129, 157, 58, 10 CONTROL "A&rchivio", 13077, BUTTON, BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 68, 169, 58, 10 CONTROL "Sistema", 13078, BUTTON, BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 129, 169, 58, 10 CONTROL "Attributi:", 13091, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 6, 157, 32, 9 CONTROL "Rinomina", 15000, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 176, 7, 43, 14 }
L'ultima riga è proprio quella che definisce il pulsante da noi aggiunto,
mentre il numero 15000 l'ho inserito manualmente io (tenetelo presente perché
ci servirà). Salviamo le modifiche, riavviamo il PC in modalità DOS e
sostituiamo il shell32.dll di windows con il file da noi modificato. Avviamo
windows, clicchiamo con il destro su un file e vediamo la dialog Proprietà...
ecco il nostro bel pulsante, che ovviamente ancora non fa niente quando lo
premiamo perché non abbiamo implementato il codice... lo faremo tra poco :-)
Adesso abbiamo il pulsante e abbiamo in mente quello che dobbiamo fare: creeremo
un programmino (io l'ho fatto in C e l'ho chiamato "renamer.exe") che
ci permetterà di rinominare un file cliccando sul bottone che abbiamo inserito,
e per far ciò dobbiamo innanzitutto modificare la Dialog Procedure della
finestra di dialogo in modo da farle gestire anche il nostro pulsante, e dopo di
ciò aggiungere a shell32.dll il codice necessario ad eseguire il renamer.
Poiché non ho voglia di andarmi a cercare una cava, ci creeremo comodamente una
nuova sezione con il PEditor, così saremo anche sicuri di non avere problemi di
spazio per inserire il codice.
Facciamo una copia di shell32.dll e apriamola con il PEditor. Clikkiamo su
"sections" e appare una finestrella con tutte le sezioni, click con il
pulsante destro su una sezione e selezioniamo la voce "add a section in the
PE header". Diamo un nome a questa sezione (con grande sforzo di
fantasia... "Spider") e poi clicchiamo con il destro sulla sezione
appena creata e selezioniamo "edit section". Impostiamo come "Virtual
Size" e come "Raw Size" il valore "1000", in modo da
avere una sezione di 1000h bytes (4 kbytes)... Sono molti di più di quelli che
ci servono, ma non è un problema perché a togliere siamo sempre in tempo.
Infine impostiamo come Characteristics il valore E0000040 per avere una sezione
eseguibile, leggibile e scrivibile. Salviamo e osserviamo che la sezione
"Spider" appena aggiunta inizia all'offset 158000, ed essendo lunga
1000h bytes si estenderà fino all'offset 159000. Prendiamo l'Hex Workshop e
andiamo all'offset 158000... Vediamo che il file finisce proprio all'offset
157FFF, quindi ci spostiamo fino alla fine del file, e aggiungiamo 1000h bytes
(ovviamente aggiungiamo zeri). Salviamo, ed ecco il nostro shell32.dll pronto
per le successive modifiche :-)
Bene, adesso che il file è pronto per essere modificato, non ci resta che... modificarlo :-). Il pulsante l'abbiamo già messo, ma chiaramente quando clicchiamo su di esso non succede nulla. Ora dobbiamo cercare la Dialog Procedure della finestra di dialogo, lì intercetteremo il messaggio WM_COMMAND e aggiungeremo il nostro codice. Prendiamo IDA e disassembliamo shell32.dll. Mentre IDA disassembla, noi riflettiamo sul da farsi. Nelle risorse di shell32.dll, la dialog che ci interessa era la numero 1041, che corrisponde in esadecimale a 411. Pertanto, da qualche parte dovremmo trovare un "push 411h" o qualcosa del genere. Appena IDA finisce di disassemblare, clikkiamo su "Search" e da lì "text...". Scriviamo 411h nell'edit apposito e mettiamo la spunta in "Find all occurrences". Premiamo OK e aspettiamo, aspettiamo, aspettiamo... ma quanto ci mette??? Ed ecco tutte le corrispondenze trovate, in totale 10. Se guardiamo il codice indicato dalle prime due, vedremo che 411h è un parametro passato a SendMessage, quindi qui non troveremo mai l'indirizzo della Dialog Procedure. Vediamo invece il codice della terza occurrence:
.text:7FD05D24 mov eax, [ebp+var_178]
.text:7FD05D2A mov [ebp+var_3E0.pfnDlgProc], offset sub_7FD05A98
.text:7FD05D34 and al, 10h
.text:7FD05D36 neg al
.text:7FD05D38 sbb eax, eax
.text:7FD05D3A and eax, 3
.text:7FD05D3D add eax, 411h
;l'occurrence è questa qui
.text:7FD05D42 mov dword ptr [ebp+var_3E0.anonymous_0], eax
.text:7FD05D48 lea eax, [ebp+var_3E0]
.text:7FD05D4E push eax ; LPCPROPSHEETPAGEA
.text:7FD05D4F call ds:CreatePropertySheetPageA
.text:7FD05D55 mov esi, eax
.text:7FD05D57 cmp esi, edi
.text:7FD05D59 jz short loc_7FD05D9F
Molto interessante... Troviamo una chiamata a
CreatePropertySheetPageA, che guardando nell'API Reference mi era già saltata
all'occhio, ma non vi avevo prestato molta attenzione. E cosa vediamo poche
righe più su??
mov [ebp+var_3E0.pfnDlgProc], offset sub_7FD05A98
Eccola!! IDA ci ha gentilmente rinominato la locazione ebp-3C8 dicendoci che è l'indirizzo di una DlgProc, proprio la Dialog Procedure che noi stavamo cercando e che si trova all'indirizzo 7FD05A98! Bene, spostiamoci a quest'indirizzo e vediamone il codice (non lo riporto tutto):
.text:7FD05AAC mov eax, [ebp+arg_4]
.text:7FD05AAF dec eax
.text:7FD05AB0 dec eax
.text:7FD05AB1 jz loc_7FD05BF9
.text:7FD05AB7 sub eax, 4Ch
.text:7FD05ABA jz loc_7FD05BC6
.text:7FD05AC0 sub eax, 5
.text:7FD05AC3 jz loc_7FD05B92
.text:7FD05AC9 sub eax, 28h
.text:7FD05ACC jz loc_7FD05B57
.text:7FD05AD2 sub eax, 95h
.text:7FD05AD7 jz short loc_7FD05B45
.text:7FD05AD9 dec eax
;se il messaggio è WM_COMMAND...
.text:7FD05ADA jz short loc_7FD05AF2 ;...
salta a 7FD05AF2
.text:7FD05ADC dec eax
.text:7FD05ADD dec eax
.text:7FD05ADE jnz loc_7FD05BDA
.text:7FD05AE4 push esi ; hMem
.text:7FD05AE5 call sub_7FD04CC3
.text:7FD05AEA
.text:7FD05AEA loc_7FD05AEA: ; CODE XREF: sub_7FD05A98+70j
.text:7FD05AEA ; sub_7FD05A98+78j ...
.text:7FD05AEA push 1
.text:7FD05AEC pop eax
.text:7FD05AED jmp loc_7FD05BDC
.text:7FD05AF2 ; ---------------------------------------------------------------------------
.text:7FD05AF2
.text:7FD05AF2 loc_7FD05AF2: ; CODE XREF: sub_7FD05A98+42j
.text:7FD05AF2 mov ecx, [ebp+hWndMain] ;mette
in ecx l'handle del controllo premuto
.text:7FD05AF5 movzx eax, cx
;lo ricopia in eax
.text:7FD05AF8 sub eax, 3313h
;sottrae 3313h
.text:7FD05AFD jz short loc_7FD05B3C ;l'utente
ha clikkato su "Sola lettura"
.text:7FD05AFF dec eax
.text:7FD05B00 jz short loc_7FD05B33 ;l'utente
ha clikkato su "Nascosto"
.text:7FD05B02 dec eax
.text:7FD05B03 jz short loc_7FD05B2A ;l'utente
ha clikkato su "Archivio"
.text:7FD05B05 sub eax, 21h
.text:7FD05B08 jnz short loc_7FD05AEA
3313h convertito in decimale equivale a 13075... riprendiamo lo script di risorse visto poco fa con Resource Hacker:
CONTROL "&Sola lettura", 13075, BUTTON, BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 68, 157, 58, 10
13075 è proprio l'IDControl della casella di
spunta "Sola lettura". L'istruzione sub
eax, 3313h è lunga esattamente 5 bytes:
giusto lo spazio che ci serve per mettere un jump short all'indirizzo della
nuova sezione, che comincia all'indirizzo in memoria 7FE08000 (come possiamo
vedere dal FLC del PEditor).
Per mettere il jump short possiamo seguire diversi metodi: potremmo utilizzare
il PHOG di Alga, potremmo assemblare direttamente da SoftICE, o ancora potremmo
calcolare manualmente la nostra opcode, che è proprio la soluzione che useremo
noi :-). L'opcode del Jump Short è E9xxxxxxxx. Poiché l'istruzione si
trova all'indirizzo 7FD05AF8 ed è lunga 5 bytes, la dword da sostituire alle x
sarà 7FE08000 - (7FD05AF8+5) = 00102503, e l'opcode completo sarà E903251000
(ovviamente i bytes sono invertiti per la notazione Intel). Prendiamo l'Hex
Workshop, andiamo all'offset 55AF8 (calcolato con il FLC del PEditor) e
sostituiamo l'opcode presente con quello da noi trovato.
Bene, adesso dobbiamo codare il codice da aggiungere nella nostra bella sezione.
Questo non dovrà far altro che richiamare un programma situato in c:\windows
(che io ho chiamato renamer.exe e che trovate nell'allegato) che si occuperà di
visualizzare una dialog per rinominare i files. Per richiamare il file useremo
l'API ShellExecute e non CreateProcess per due motivi: la prima richiede meno
parametri, ed inoltre si trova nella shell, così non avremo problemi con i
thunk in quanto potremo mettere una call diretta. Per informazioni su
ShellExecute vi rimando alla vostra API Reference. Vediamo comunque il prototipo
dell'API:
HINSTANCE ShellExecute(
HWND hwnd,
// handle to parent window
LPCTSTR lpOperation, // pointer to string that specifies operation to perform
LPCTSTR lpFile,
// pointer to filename or folder name string
LPCTSTR lpParameters, // pointer to string that specifies executable-file parameters
LPCTSTR lpDirectory, // pointer to string that specifies default directory
INT nShowCmd // whether file is shown when opened
);
Per assemblare il codice io ho usato direttamente softice, in modo da non avere
problemi con il calcolo delle opcode dei salti. Dopo aver inserito il nostro
codice, lo dumperemo con icedump (caricatelo, se non lo avete ancora fatto) e lo
inseriremo dall'editor esadecimale all'interno di shell32.dll. Mettiamo un
breakpoint su una qualunque API richiamata da shell32.dll, ad esempio
CreatePropertySheetPageA che abbiamo visto poco fa, clikkiamo con il destro su
un qualunque file e selezioniamo la voce "Proprietà"... ecco il
nostro debugger preferito che ci poppa sullo schermo. Premiamo F12 e ci
troveremo nel processo di shell32.dll. Digitiamo "a 7FE08000" e
assembliamo il seguente codice:
cmp eax, 3A98 ;3A98h
= 15000 decimale
jz 7FE08011 ;----\
sub eax, 3133 ;| sub eax,3133
l'avevamo tolto nel vecchio codice
jmp 7FD05AFD ;| torniamo al
vecchio codice
push 1 ;<---/
push 01234567 ;lpDirectory (per adesso
mettiamo solo un valore dummy)
push 01234567 ;lpParameters (dummy)
push 01234567 ;lpFile (dummy)
push 0
push 0
call ShellExecuteA
mov ax, 3A98 ;rimettiamo 3A98 per essere sicuri che la
Dialog Procedure non lo gestisca
jmp 7FD05AFD
Prima di tutto controlliamo che il WM_COMMAND sia dovuto alla pressione del nostro pulsante, confrontando eax con 3A98 che è, appunto, l'ID del Button che abbiamo inserito. Se l'ID è diverso, eseguiamo il sub eax,3133 che avevamo tolto e torniamo al vecchio codice. Se invece l'ID è quello del nostro pulsante, chiamiamo ShellExecuteA, dovo aver ovviamente pushato i parametri. Come lpFile, lpDirectory e lpParameters impostiamo per adesso dei valori dummy in quanto dobbiamo mettere in memoria i dati necessari. Poichè il codice che abbiamo inserito finisce all'indirizzo 7FE08033, mettiamo il nome del file e la directory in sequenza a partire dall'indirizzo 7FE08034. La stringa "renamer.exe",0 inizia all'indirizzo 7FE08034, mentre "c:\windows\",0 inizia all'indirizzo 7FE08040. Aggiorniamo quindi i push con questi indirizzi:
push 7fE08040 ;lpDirectory
push 01234567 ;lpParameters (dummy)
push 7FE08034 ;lpFile
E abbiamo anche lpDirectory e lpFile... adesso
però ci serve lpParameters, cioè i parametri da passare alla CommandLine del
nostro renamer. Infatti tramite CommandLine dobbiamo passare il nome del file
che si vuole rinominare, ma... da dove prendiamo questo nome???
Per adesso dumpiamo il codice appena inserito con
"/dump 7FE08000 100 c:\dump.bin", modifichiamo shell32.dll e
sostituiamolo (ovviamente da DOS) al file shell32.dll originale.
Per passare l'ultimo parametro mancante
dobbiamo prelevare il nome del file interessato da qualche altra parte. Windows
utilizzerà sicuramente qualche API per prelevare alcune informazioni sul file,
come i suoi attributi, la data dell'ultima modifica, ecc. E' chiaro che per
individuare il file passerà alle suddette API un puntatore al nome del file
stesso... E noi ne approfitteremo per prelevare il nome del file e copiarlo da
qualche parte per poterlo poi utilizzare a nostro vantaggio :-))
Adesso ci resta solo di capire QUALI funzioni vengono utilizzate da windows per
lavorare sul file. Dopo innumerevoli breakpoint, sono riuscito a breakare su
SHGetFileInfoA: come al solito clikkiamo su un file con il pulsante destro,
selezioniamo la voce "Proprietà", e prima di cliccarvi entriamo nel
softice e digitiamo "bpx SHGetFileInfoA", premiamo F5 e clickiamo su
"Proprietà". Sice poppa e noi premiamo F12. Vediamo che ci troviamo
all'indirizzo 7FD0578A. Riprendiamo il disassemblato di IDA e vediamo cosa c'è
in quella zona di codice e un po' prima:
.text:7FD05737 push ebp
.text:7FD05738 mov ebp, esp
.text:7FD0573A sub esp, 434h
.text:7FD05740 push ebx
.text:7FD05741 push esi
.text:7FD05742 mov esi, [ebp+arg_0]
.text:7FD05745 lea eax, [ebp+dwFileAttributes]
.text:7FD0574B push edi
.text:7FD0574C push eax ; lpFindFileData
.text:7FD0574D lea ebx, [esi+3Ch]
.text:7FD05750 push ebx ; lpFileName
.text:7FD05751 call ds:FindFirstFileA
.text:7FD05757 cmp eax, 0FFFFFFFFh
.text:7FD0575A jnz loc_7FD05981
.text:7FD05760 push 50h
.text:7FD05762 xor eax, eax
.text:7FD05764 pop ecx
.text:7FD05765 lea edi, [ebp+dwFileAttributes]
.text:7FD0576B repe stosd
.text:7FD0576D
.text:7FD0576D loc_7FD0576D: ; CODE XREF: sub_7FD05737+251j
.text:7FD0576D push 710h
.text:7FD05772 lea eax, [ebp+wParam]
.text:7FD05778 push 160h
.text:7FD0577D push eax
.text:7FD0577E push [ebp+dwFileAttributes]
.text:7FD05784 push ebx
.text:7FD05785 call SHGetFileInfoA
.text:7FD0578A push offset a_ani ; ".ani"
.text:7FD0578F push ebx
Un po' più su della chiamata a SHGetFileInfoA
troviamo una chiamata a FindFirstFileA. Come vediamo in ebx viene messo un
puntatore al nome del file... cioè proprio quello che serve a noi :-).
Sostituiamo la call a FindFirstFile con una call a 7FE08050 (aggiungiamo un nop
per pareggiare i conti) dove inseriremo il nuovo codice, cioè questo:
:7FE08050 pushad
;salviamo tutti i registri
:7FE08051 mov ecx, 7FE080B0 ;a
7FE080B0 copieremo la path che ci serve
:7FE08056 mov al, [ebx]
:7FE08058 mov [ecx], al
:7FE0805A inc ebx
:7FE0805B inc ecx
:7FE0805C cmp al, 0
:7FE0805E jnz 7FE08056
:7FE08060 popad
;ripristiniamo tutti i registri
:7FE08061 call FindFirstFileA ;rimettiamo la
call per non alterare il funzionamento del file
:7FE08066 jmp 7FD05757 ;torniamo al vecchio codice
Ecco fatto. Adesso quando sarà eseguita la call a ShellExecute avremo, a
partire dall'indirizzo 7FE080B0, il nome del file da rinominare, che potremo
quindi passare alla CommandLine del renamer. Aggiorniamo quindi anche il vecchio
codice:
cmp eax, 3A98 ;3A98h
= 15000 decimale
jz 7FE08011 ;----\
sub eax, 3133 ;| sub eax,3133
l'avevamo tolto nel vecchio codice
jmp 7FD05AFD ;| torniamo al
vecchio codice
push 1 ;<---/
push 7FE08040 ;lpDirectory
push 7FE080B0 ;lpParameters
push 7FE08034 ;lpFile
push 0
push 0
call ShellExecuteA
mov ax, 3A98 ;rimettiamo 3A98 per essere sicuri che la
Dialog Procedure non lo gestisca
jmp 7FD05AFD
Ridumpiamo, rimodifichiamo shell32.dll e codiamo
il nostro bel renamer.exe (i sorgenti in C li trovate nell'allegato), il quale
andrà messo in c:\windows, e infine godiamoci i frutti del nostro lavoro :-)
Note finali |
Saluto Quake2 che mi ha dato qualche consiglio su quali breakpoints usare :-) e poi saluti a tutti, AndreaGeddon, Quequero, +Malattia, Yado, [kill3xx], TheMR, Bubbo, Case, albe, _Blackdeath_, ph0b0s, Cieli Sereni, DsE, cHr, True-love, CrazyKiwi, _d31m0s_, Ritz, Pbdz, x86, NikDH, Dark-Angel, Dades, CyberPK, Guzura, littleluk, il mio amico BluDestiny e tutti quelli che in questo momento non ricordo.
Disclaimer |
Vorrei ricordare che il software va comprato e non rubato, dovete registrare il vostro prodotto dopo il periodo di valutazione. Non mi ritengo responsabile per eventuali danni causati al vostro computer determinati dall'uso improprio di questo tutorial. Questo documento è stato scritto per invogliare il consumatore a registrare legalmente i propri programmi, e non a fargli fare uso dei tantissimi file crack presenti in rete, infatti tale documento aiuta a comprendere lo sforzo immane che ogni singolo programmatore ha dovuto portare avanti per fornire ai rispettivi consumatori i migliori prodotti possibili.
Noi reversiamo al solo scopo informativo e di miglioramento del linguaggio Assembly.