Bravo ritz, eccovi un ottimo esempio di come
personalizzarsi win =)
UIC's form
E-mail: ritz@freemail.it
Ritz, #crack-it
UIC's form
Difficoltà
(
)NewBies (X)Intermedio (X)Avanzato (
)Master
In questo tute spiegherò come reversare l'orologio del tray di Windows in
modo da fargli segnare l'ora che vogliamo noi (precisamente i beat).
WinClock reversing (hooking,
dirottamento e manipolazione dell'ora del tray) Written by Ritz
Introduzione
Salve raga!! Dovete sapere (tanto so che non ve ne frega un cazz ;) ) che
qualche tempo fa un amico mi fece una domanda un po' strana, che sinceramente
non mi era mai saltata per la testa, ovvero se era possibile fare in modo che
l'orologio di Windowz (quello del tray) potesse essere "convertito" in modo che
segnasse l'ora in beat. La cosa mi incuriosì molto, così dopo qualche giorno
decisi di provare a dare un'occhiata su come win gestisce il clock del tray per
vedere se (e come) si sarebbero potute apportare delle modifiche per far
funzionare l'orologio come volevamo noi... La cosa era sicuramente più insolita
e più interessante del puro cracking riferito alle protezioni dei programmi, e
così, dopo alcuni giorni di impegno... eccovi questo tute:)). Perchè fare una
cosa del genere? Beh è vero che non servirà a nulla in senso pratico (anche se
non è detto), ma in ogni caso penso sia interessante capire come agisce Windoz
anche in alcune situazioni apparentemente banali come appunto questa. Fatte ora
queste dovute (????) considerazioni, passiamo pure al reversing vero e
proprio.
Spero che sappiate a cosa serva il clock del tray di windows:).
Essay
Bene raga, che ne dite di vivisezionare un pochetto quella bell'iconetta
del tray che ci mostra l'ora? Hihihihihihi bene partiamo pure:))
Qual è
il nostro obiettivo? Beh è semplice: vogliamo reversare l'orologio (d'ora in
poi sarà sottinteso orologio del tray) in modo che riporti l'ora in beat
appunto e non in formato "convenzionale".
Ah dimenticavo! Per chi non
lo sapesse il beat (simbolo @) è (anzi, vorrebbe essere) un nuovo metodo di
misurazione del tempo, secondo il quale una giornata viene divisa in 1000
parti uguali (1000 beat da 0 a 999 appunto), creato dalla Swatch.
Oki,
almeno ora abbiamo le idee un po' + chiare sul da farsi. Dunque,
concettualmente per raggiungere lo scopo bisognerà (naturalmente) passare
varie fasi, + precismente bisognerà:
1- Scoprire quale funzione scrive
l'ora nel tray.
2- Dirottare la funzione (prima che l'ora venga
scritta) in un punto dove prenderà inizio la routine di
manipolazione.
3- Scrivere la routine di manipolazione nel punto in cui
è stata dirottata la funzione incriminata ;).
4- Far tornare il
controllo come nulla fosse accaduto.
PRIMA di far tutto ciò bisognerà
inoltre osservare bene con l'Ice la situazione che si presenta nella zona da
cui partirà il dirottamento per prestare attenzione a come scrivere la nostra
routine (hihihihi poi capirete perchè).
Bene, innanzi tutto dovremo
capire quale API scrive l'ora nel tray. Beh non ci vuole molto per capire che
sarà un'API che scrive del testo... contando quindi nell'intuito del coder
(erm... chiamiamolo pure "culo") mettiamo un bpx su SetWindowTextA una volta
che siamo nel desktop (possibilmente senza prog. in esecuzione). Aspettiamo un
po' ed ecco che il Sice poppa proprio quando il clock si aggiorna di un
minuto... Quando si dice coincidenza :))
Ecco la
situazione:
:00406684 56 push esi :00406685 FF7508 push
[ebp+08]
* Reference To: USER32.SetWindowTextA,
Ord:0259h | :00406688 FF15C4124000 Call dword ptr
[004012C4] :0040668E 668B45F8 mov ax, word ptr [ebp-08] :00406692
663B053A524100 cmp ax, word ptr [0041523A] :00406699 752E jne
004066C9
* Referenced by a (U)nconditional or (C)onditional Jump at
Addresses: |:004066C7(U), :004066E5(U) | :0040669B 0FB74DFC movzx
ecx, word ptr [ebp-04] :0040669F 69C9E8030000 imul ecx,
000003E8 :004066A5 B860EA0000 mov eax, 0000EA60 :004066AA 5E pop
esi :004066AB 2BC1 sub eax, ecx :004066AD C9 leave :004066AE C20400
ret 0004
Hoho che bello... non ci avrei sperato... la sintassi
dell'API è tutta lì che ci aspetta... beh vabbè non è che sia così complicata
:) Esi punterà naturalmente al testo da scrivere e ebp+08 sarà l'handle. Esi
infatti punta proprio al valore aggiornato da scrivere ("d esi" dal Sice). Beh
fin qui sembrerebbe quasi troppo facile. Sappiamo che Esi punta alla stringa
da scrivere, e sarà proprio Esi quindi il nostro punto di
riferimento.
Osserviamo facilmente che l'ora puntata da esi è scritta
semplicemente come stringa ASCII (es. 17.23).
Steppiamo un po' per
vedere cosa ci appare in seguito di fronte.
:00406533 8945F8 mov
dword ptr [ebp-08], eax
* Referenced by a (U)nconditional or
(C)onditional Jump at Address: |:00406529(C) | :00406536 8D45DC lea
eax, dword ptr [ebp-24] :00406539 50 push eax :0040653A FF7508 push
[ebp+08]
Si vede infatti che con la semplice chiamata
SetWindowTextA la stringa non viene stampata subito nel tray, ma al contrario
prima che ciò avvenga bisogna passare attraverso altre API quali
GetClientRect, SelectObject, GetSysColor, SetBkColor, SetTextColor,
GetTextExtentPointA e finalmente ExtTextOutA, che stampa la benedetta ora. Non
penso serva spiegare cosa fa ognuna di queste API (al massimo guardatevi
un'API Reference), ma è interessante notare che proprio ExtTextOutA è
responsabile dell'apparizione dell'ora nuova. Eccone la sintassi:
BOOL
ExtTextOut(
HDC hdc, // handle to device context
int X, // x-coordinate of reference point int
Y, // y-coordinate of reference point UINT
fuOptions, // text-output options CONST RECT
*lprc, // optional clipping and/or opaquing rectangle
LPCTSTR lpString, // points to string UINT
cbCount, // number of characters in string CONST INT
*lpDx // pointer to array of intercharacter spacing
values );
Beh non semplicissima a prima vista, ma cmq a noi basta
sapere che l'ultimo parametro pushato è l'handle e il 3° punta alla stringa da
scrivere... perchè dico questo? Beh perchè sia in quest'API che nelle altre
sopra bisogna fare MOLTA attenzione a una cosa... prima che venga chiamata
SetWindowTextA infatti ebx ha un certo valore che NON cambia fino ad arrivare
a ExtTextOut... esso infatti è proprio l'handle dell'oggetto dove porre il
testo, e viene chiamato anche in varie altre API precedenti. Di conseguenza
esso NON deve ASSOLUTAMENTE essere modificato dalla nostra routine, a patto di
non essere salvato in qualche altro punto, ma cmq ciò non è necessario in
quanto oltre a esi bastano solamente un altro paio di registri, l'immancabile
eax e ecx ad esempio.
Ora che abbiamo capito la situazione, che
facciamo, prima scriviamo la routine di conversione o prima troviamo lo spazio
dove metterla? Meglio la prima credetemi ;).
Allora i modi per
procedere sono più d'uno (almeno penso), ma io ho cercato di attuare quello +
semplice. Abbiamo detto che l'ora è sotto semplicissima forma ASCII nel
buffer. Ciò significa che se ad esempio sono le 17.23 esi punterà a un buffer
contenente
Che schifo
di disegno... vabbe non importa... beh non sarebbe + semplice poter avere
tutti i valori ASCII espressi in byte puri per manipolarli (ad esempio invece
che 37h (7) avere proprio 7h)? Beh secondo me si (o almeno è ciò che ho
pensato da subito). Ecco quindi che innanzi tutto sarà necessario togliere a
ogni byte dell'ora (tranne il punto ;) il valore 30h. Ora la situazio
sarà
1 7 2E 2 3
invece quella
sopra.
Ragionando semplicemente in dec, per attuare la conversione
basta calcolare i secondi totali che compongono un dato orario e quindi
dividerli per un certo valore. Per trovare tale valore basta considerare che
alle 23.59 i beat dovranno essere 999, quindi 23.59 = 86340 sec, 86340 / 999 =
86,42...... Il numero da usare per convertire un certo numero di secondi in
beat è infatti 86,42. Per essere + precisi moltiplicheremo sia il valore da
convertire che il fattore di conversione per 10 prima di effettuare
l'operazione. Beh, adesso sappiamo anche come farlo in dec, per farlo in hex
basterà semplicemente sostituire a tutti i valori dec quelli hex
corrispondenti, nulla di più semplice. Ecco che la routine prende
forma...
xor eax, eax |- xor ecx, ecx | mov al, byte ptr
[esi+4] ;ultimo valore dell'ora | sub al,
30h ;valore ASCII ->
byte | imul eax, 3Ch
;secondi totali | mov ecx,
eax |- xor eax, eax |
mov al, byte ptr [esi+3] ;viene preso il numero delle decine di | sub al,
30h ;minuti, convertito in secondi e | imul eax, 258h ;aggiunto al valore
finora trovato [x*A(10d)*3C(60d)] | add ecx, eax |- xor eax, eax |
mov al, byte ptr [esi+1] ;stessa cosa per le ore | sub al, 30h | imul
eax, E4BC0h | add ecx, eax |- xor eax, eax | mov al, byte ptr esi ;e
le decine di ore | sub al, 30h | imul eax, 8CA0h |- add ecx,
eax imul ecx, 10h mov eax, ecx mov ecx , 566h cdq idiv
ebx
Fatto ciò avremo in eax il valore hex che convertito in dec sarà
proprio il nostro orario. Adesso bisogna fare in modo che tale valore venga
appunto convertito e poi stampato al posto dell'ora convenzionale. La cosa è
fattibile con una particolare API che fa tutto ciò al posto nostro,
precisamente wsprintfa. Eccone la sintassi:
int wsprintf(
LPTSTR
lpOut, // pointer to buffer for output LPCTSTR
lpFmt, // pointer to format-control string
... // optional arguments );
Quest'api infatti
prende da un registro il valore che convertito sarà la stringa da scrivere, lo
converte e lo salva NON come byte ma proprio come stringa ASCII in un buffer.
Bello eh? :). Beh in questo caso i parametri da pushare saranno tre,
eccoli:
mov [esi+07], 6425 1 push eax add esi,
7 2 push esi sub esi, 7 add esi, 1 3 push esi sub esi,
1 call wsprintfa
Eax contiene proprio la stringa
famosa, esi (2° parametro) sarà il tipo di formato della stringa che vogliamo
avere, noi metteremo %d (scusate l'estetica orrenda per pushare il 2°
parametro, mi faccio schifo da solo ma quel giorno non avevo proprio voglia di
andarmi a cercare come si pusha in linguaggio macchina l'offset
esi+7).
Beh adesso in esi+1 avremo la nostra bella stringa pronta per
essere visualizzata a schermo.
Che fare adesso?
Ora basta
semplicemente formattarla a piacere, ovvero scegliere il layout preferito ;) .
Convenzionalmente (anzi, meglio forse dire "usualmente") l'ora in beat viene
espressa col simbolo @ davanti all'ora (es. @123), quindi basterà con una
semplicissima routine fare proprio una cosa del
genere:
Beh, semplicemente
qui viene messo proprio il simbolo "@" prima del valore, una cosa da notare è
che al posto degli eventuali byte indesiderati NON deve venir messo 00h, in
quanto la funzione ExtTextOut non si basa sul byte 00 di terminazione stringa
per delimitare i charz da scrivere, bensì essi devono essere definiti
attraverso un normale push. Di conseguenza sarà opportuno mettere degli spazi
(mettendo 00h al posto di 20 il clock visualizza infatti un quadratino tipo
).
Infine basterà chiamare SetWindowTextA (con relativi push) e far
tornare l'esecuzione della routine dove era stata interrotta. Ecco come si
presenta la mia routine di conversione:
Naturalmente non è molto ottimizzata, anzi. Infatti
sfortunatamente non è nemmeno definitiva. Come mai? Beh, semplice,
modificatevi il file in questo modo (creando una nuova sezione, usando i byte
in coda al file, insomma come volete) e cambiate l'ora alle 23.59... @999,
bene, aspettate la mezzanotte;) dovrebbe apparire @000... nooooo perchè non
appare? Al posto suo compare un numero che non centra un cazzo (mi pare
qualcosa tipo @688), perchè mai? Hehe, fino ad ora non abbiamo considerato
una cosa: in windoze le ore con 1 cifra delle ore (es. 9.32) vengono
visualizzate come x.yz, NON come 0x.yz. Bella sfiga per noi. Che fare dunque?
Basta semplicemente traslare fisicamente ;) i byte dell'ora comune in modo che
si mettano al giusto posto. Esempio:
Puntando esi infatti a una sequenza del tipo tx.yz, se t non esiste
allora l'ora sarà del tipo x.yz, quindi il 4° byte sarà 00h. Se accade ciò,
allora il prog mette in eax il valore dei 4 byte del buffer e li rimette poi
in esi+1, scalandoli in pratica di 1 posizione. Fatto ciò, basterà mettere uno
00h al byte puntato da esi. Bene, a prima vista adesso dovrebbe essere tutto a
posto, vero? Hum chissà perchè mi sa proprio di no ;). Il problema questa
volta ce lo dà l'API wsprintfA, in quanto sfortunatamente se la stringa
risultante è di 2 cifre invece che 3 allora lo 0 iniziale non lo mette neanche
:((. Moltobbbbbeeeeeeneeeee, cerchiamo di vedere anche questa
situazione. Se la stringa risultante è di 3 charz, l'API nel buffer puntato
da esi scrive una roba del tipo "n111" (5° byte null), se invece essa è di 2
charz scrive una cosa del tipo "011" (il 4° byte è null), se poi è di 1 solo
char allora è del tipo "01" (3° byte null). Cosa significa tutto ciò?
Significa che anche in questo caso ci troviamo scombinati di 1 o 2 byte. Come
prima bisognerà porre rimedio in modo simile:
Tutto
molto semplice: in tale maniera si fa in modo di tornare alla forma originale
del tipo "n111". Bene, fatto ciò la nostra routine (stavolta completa) si
dovrebbe presentare così:
Ecco la versione finale
disassemblata... senza vari nop (ne ho usati un casino :). In questo caso
viene usata la miriade di byte finali in coda al file (un peccato sprecarli
;), ma naturalmente si sarebbe potuto aggiungere benissimo una nuova sezione,
solo che questo avrebbe dato dei problemi per fare la patch. Il codice sopra
natralmente non è per nulla ottimizzato, sono stati sprecati molti byte,
l'ottimizzazione fatela come meglio credete.
A proposito di patch, se
adesso vogliamo distribuire la nostra copia dell'exploder nuovo? Beh scrivere
un patcher in qualsiasi linguaggio 16bit (infatti essa sarà eseguita
necessariamente da dos) non è per nulla complicato, si può fare in asm,
pascal, c, insomma ciò che volete. Il problema sono la quantità enorme di byte
da cambiare. Per questo può usare qualche utile prog. tipo patcher engine di
+MaLa ad esempio, che ci toglie molto lavoro inutile.
Beh, a questo
punto la missione sembra conclusa, il clock conta il tempo in beat, ma l'ora
naturalmente si può ancora regolare in tempo convenzionale.
Considerazioni finali e saluti
In quanto a considerazioni finali che dire, questo è stato solo un esempio di
come poter utilizzare l'asm per cambiare il nostro amato (si fa per dire)
Windoze e fargli fare, almeno in parte, quello che vogliamo noi, discorso che
vale naturalmente per tutti gli altri programmi.
Per quanto riguarda i ringraziamenti e saluti, cose da dire ne ho varie.
Ringrazio specialmente:
§ +MaLaTTiA: per la grandissima disponibilità (beta-testing compreso) e
sostegno morale, e perchè finalmente ogni tanto lo si vede su irc ;)).
§ Kill3xx: perchè mi ha tolto dal dubbio di come convertire eax in ASCII
usando wsprintfa ;).
§ Quequero: per i vari consigli asm compreso quello sopra.
§ cod: v.s. :)
§ Yado: anche lui per vari cosigli asm & betatesting.
§ -NeuRaL_N.: per l'OpGen, utilissimo nei salti.
§ BlackDruiD: perchè non scoprirò mai come cazzo fa a fare le sue magie e
perchè è troppo disponibile a farmele ;) (pure per il betatesting).
Saluto inoltre i vari [aLT255], AndreaGeddon, Int19, TiN_MaN, ^courier,
grisu, nobody88 e tutti gli altri che sanno di dover essere salutati;).
Beh prima di finire un particolare ringraziamento va pure a colui che mi ha
dato l'insana idea di fare tutto ciò: ciao Defra!!
Bene, se volete mandare critiche, far domande, ecc... fatelo pure a
ritz@freemail.it.
Ve saludo!!!
@2000 by Ritz
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.
UIC's page of reverse engineering, scegli dove
andare: