Piccolo reversing della calcolatrice di Win
(Aumentiamo la funzionalità della calcolatrice facendola comunicare con un nostro)
mino)

Data

by "Guzura"

 

12/01/2001

UIC's Home Page

Published by Quequero


La fine del mondo sarà un nuovo pretesto  per proporci un nuovo e rivoluzionario Windows Olocaust ...

Il tute a dire il vero non parla molto di reversing, il tutto si svolge nel campo della programmazione, sono trattate cmq molto bene API come SendMessage che spesso danno moooolti problemi e RegisterWindowMessage che a volte può risultare oscura, è anche bella l'idea di presentare un tutorial che si preoccupi di spiegare come far comunicare un programma con un altro senza intaccarlo a livello di codice, bravo Guz

Oppure vedete meglio un Windows Armageddon...

....

E-mail: [email protected]
Guzura su #crack-it irc.estranet.it

....

Difficoltà

( )NewBies (X½)Intermedio ( )Avanzato ( )Master

 

Questo non è assolutamente un tutorial che parla di come Crackare la calcolatrice di Windows ;))))) ma di come aggiungere invece funzionalità a quest'ultima e fare in maniera che comunichi BIDIREZIONALMENTE con una nostra applicazione


Piccolo reversing della calcolatrice di Win
(Aumentiamo la funzionalità della calcolatrice facendola comunicare con un nostr
o programmino)
Written by Guzura

Allegato

Introduzione

La nascita di questo progetto ha avuto un parto datato nella mia testa e risale al periodo nel quale iniziai interessarmi di Cracking/Reversing (circa 10 mesi fà) e una delle difficoltà maggiori che trovai era quella di  avere velocemente il risultato di operazioni matematiche come ROL eax,15 (che il Que ci infila in maniera copiosa nei sui Crackme); l'operazione in sè è stupida ma se dovete farvela a mano (tante volte) è cmq una rottura di balle... L'unico strumento, forse il più utile che la MS ci da nel paco Win, è la calcolatrice di Windows che comunque non gestisce ROL, ROR e compagnia bella ma almeno ha gli XOR e LSH (o SHL) ma provate a fare con la calcolatrice EEEEEEEE LSH 33333 o simili e dopo poco vi salta fuori una MBoxA che vi annuncia che l'operazione potrebbe richiedere molto tempo, scelgliete Termina e così vi si impalla del tutto la calcolatrice...(Testing non sembrava il caso di farne???...).
Ok nasce così (ora che ne so di più di programmazione C per Win32) il mio CalcExtender 1.0 (Bel nome eh ?? ;))))))) che gestisce OR, XOR, SHL, SHR, ROL, ROR, IDIV, IMUL ed è fatto in maniera da utilizzare valori ESADECIMALI ma mancano le varie conversioni e tutte le altre utilità che invece servono a una calcolatrice. Da qui l'idea di rendere la calcolatrice di Win interfacciabile con la mia utility in modo che cio che non faccio con una lo faccio con l'altra (se poi riuscite ad applicare questa filosofia con le donne allora siete a posto scrivete un tut e poi mandatemelo;))))).
 
Un paio di notizie sul mio programmino:
1) e la versione 1.0 e se vi farà schifo (come temo) rimarrà anche la sola release
2) è ancora sensibile a errori di OVERFLOW soprattutto sulla IDIV (devo installare un SEH Handler)
3) richiede una CalcDll.Dll scritta in ASM che va messa nella directory di Windows che mi serviva per essere sicuro che le operazioni fossero eseguite nella maniera corretta
4) vi rimando a un file TXT esplicativo nel pacco (se volete scaricarlo)

Tools usati

SADD  by Neural_Noise
OPGEN   by Neural_Noise
Hiew   per patchare la Calcolatrice di Win
Lcc-win32    compilatore C per Win32 free che è una bomba (Se volete far delle prove o modificare il mio codice)
Spy++    Per individuare la ClassName dei programmi
Masm32       se volete modificare la DLL
IDA    per disassemblare la calcolatrice
Manuale API di riferimento
Conoscenza di un pò di C per Win32 (Se scaricate Lcc-Win32 c'è un bellissimo tutorial sulla programmazione C per Win32 allegato) 
Exescope o simili per guardare gli ID associati ai tasti e alle varie risorse

URL o FTP del programma

Sul vostro PC dovreste averla la calcolatrice!!!!

Notizie sul programma

Riesce a fare 1+1=2

Essay

1) PRIMA FASE: NOI COMUNICHIAMO CON LA CALCOLATRICE
Quello che voglio è trovare un sistema per comunicare un valore elaborato dal mio programmino alla calcolatrice e fare in modo che questo valore venga visualizzato nella EditBox dove appaiono i valori quando si usa la calcolatrice in maniera normale.
L'idea è mandare dei WM_COMMAND alla calcolatrice e simulare la pressione di un tasto così se devo far avere alla calcolatrice il valore 3AE566 dovrò mandare 6 WM_COMMAND che simulino la pressione del tasto 3 sulla calcolatrice, del tasto A, del tasto E e così via.
Vediamo di analizzare tutta la procedura da seguire e le API che ci servono.
Primo passo: Verifica del caricamento della calcolatrice e individuazione dell'Handle della finestra associata alla calcolatrice
Passo secondo: Invio dei messaggi
 
PRIMO PASSO
Per avere il valore dell'handle della calcolatrice (se è caricata) possiamo usare l'API FindWindow (Nella stessa maniera usata da Que nel tutorial sul memory patching) che è definita come segue :
 
HWND FindWindow(
LPCTSTR lpClassName,    // pointer to class name
LPCTSTR lpWindowName     // pointer to window name
);   
La ClassName è ottennibile attraverso lo Spy++ o qualsiasi altro Spyer (io uso vaSpy) mentre la WindowName che non è altro che il titolo della finestra ("Calcolatrice" nel nostro caso) potete anche lasciarlo NULL,a patto che la ClassName sia ben definita e non identifichi altre window ma per la calcolatrice è così.
 
SECONDO PASSO
Ora possediamo l'handle alla finestra della calcolatrice, per mandare i messagi usiamo la API SendMessage che è definita così:
 
LRESULT SendMessage(

HWND hWnd,    // handle of destination window
UINT Msg,    // message to send
WPARAM wParam,    // first message parameter
LPARAM lParam     // second message parameter
);
 
L'handle della finestra è ovviamente quello che abbiamo ottenuto dalla FindWindow, il messaggio è invece il WM_COMMAND e proprio nella definizione del WM_COMMAND troviamo anche quali sono i parametri (WPARAM e LPARAM) aggiuntivi che dobbiamo inserire per simulare la pressione di un tasto.
Vediamo la definizione del WM_COMMAND e del perchè utilizziamo proprio questo:
 
WM_COMMAND message è mandato quando l'user seleziona un command item da un menu, quando un control manda un notification message alla sua parent window, o quando un accelerator keystroke viene tradotto.

WM_COMMAND
wNotifyCode = HIWORD(wParam); // notification code
wID = LOWORD(wParam); // item, control, or accelerator identifier
hwndCtl = (HWND) lParam; // handle of control

Parameters
wNotifyCode
Value of the high-order word of wParam. Specifies the notification code if the message is from a control. If the message is from an accelerator, this parameter is 1. If the message is from a menu, this parameter is 0.

wID
Value of the low-order word of wParam. Specifies the identifier of the menu item, control, or accelerator.

hwndCtl
Value of lParam. Identifies the control sending the message if the message is from a control. Otherwise, this parameter is NULL.
 
In realtà non sappiamo con esattezza se la calcolatrice quando manda un WM_COMMAND setta dei particolari valori per il NotifyCode e per LParam ma di sicuro l'idinteficatore (ID) del tasto definisce con certezza il controllo che ha mandato quel WM_COMMAND.
Quello che intendo è questo, nella maggior parte dei casi un programma se ne sbatte dei questi parametri e si preoccupa solo del wID, e in base a questo agisce di conseguenza. 
 
Quindi se dovrò mandare un messaggio userò SendMessage(WinCalcHandle,WM_COMMAND,wID,0) e simulerò così la pressione del tasto corrispondente a quel wID (occhio che WParam è una DWORD,wID è una word,con questa assunzione in pratica intendo che il NotifyCode è 0 e quindi costruisco la DWORD di WParam come 0000-wID).   
 
Non ci resta che trovare i wID relativi ai tasti che ci interessa simulare (cioè il tastierino numerico della calcolatrice).
Per far questo sfruttiamo Exescope. Spero sappiate usarlo; trovate il Dialog della calcolatrice e cercate il tasto che vi interessa e scrivetevi l'ID (per il tasto A è 134 in decimale). 
 
A questo punto non ci resta che mandare i SendMessagge; supponiamo di voler mandare la stringa ABC la sequenza sarà questa:
SendMessage(WinCalcHwnd,WM_COMMAND,134,0);//134 è il wID del tasto A nella calcolatrice
SendMessage(WinCalcHwnd,WM_COMMAND,135,0);//135 è il wID del tasto B nella calcolatrice
SendMessage(WinCalcHwnd,WM_COMMAND,136,0);//136 è il wID del tasto C nella calcolatrice
 
Ora vi riporto uno spezzone di codice "simile" a quello che trovate nell'allegato che implementa la trasmissione di una stringa esadecimale
case IDSEND:        //se ho premuto il tasto Send nel mio proggy mando un valore alla calcolatrice
                {
                    WinCalcHwnd=FindWindow("SciCalc",NULL);         //becco l'handle della finestra della calcolatrice
                    if (WinCalcHwnd==NULL) MessageBoxA(hwndDlg,"Calcolatrice non caricata","Error 6",MB_OK);//avverto se non è caricata
                    else MessageBoxA(hwndDlg,"Calcolatrice caricata","Ok",MB_OK)
                              
                                verifica=VerificaErroriRegistri(hwndDlg);     //mia funzione che verifica che la stringa che voglio mandare è esadecimale 
                                if (verifica==1) {
    int counter;
    SendMessage(WinCalcHwnd,WM_COMMAND,143,0);
    for (counter=0;counter<8;counter++)
        {
            switch (Reg[counter]) //Reg e un array di 9 BYTE , 8 BYTE uno per CHAR  più un BYTE per il terminatore che contiene la stringa da mandare (ogni 2 CHAR è come se descrivessi un BYTE in un Registro)
            {
                case 'A': SendMessage(WinCalcHwnd,WM_COMMAND,134,0);break;//134 è il wID del tasto A nella calcolatrice
                case 'B': SendMessage(WinCalcHwnd,WM_COMMAND,135,0);break;
                case 'C': SendMessage(WinCalcHwnd,WM_COMMAND,136,0);break;
               //... ho tagliato gli altri
                case '9': SendMessage(WinCalcHwnd,WM_COMMAND,133,0);break;

                default :{
                            if (Reg[counter]=='\0') counter=9;
                            else {
                                MessageBoxA(hwndDlg,"Il valore non è Esadecimale","Errore 2",MB_OK);
                                counter=9;
                                }
                    break;};
            }
                                //PostMessage(WinCalcHwnd,WM_QUIT,0,0)    vi chiude la calcolatrice ;)))) ma non funziona con SendMessage
                            }
                }
                return 1;
Un paio di chiarimenti ancora sul codice sopra che servono solo per spiegare i check di corretezza adottati che a questo livello sono anche ridondanti: per prima cosa è verificata la presenza della calcolatrice, poi con una funzione (che trovate nel sorgente del programma) verifico che alla stringa corrisponda una DWORD esadecimale, poi mando un WM_COMMAND che simula l'attivazione della codifica esadecimale nella calcolatrice di Win altrimenti questa non processa i caratteri non numerici (in teoria dovrei mandare anche un messaggio di cancellazzione di una stringa eventualmente presente altrimenti la mia stringa  si accoderebbe a quella presente ...) dopo di che con un ciclo FOR e un SWITCH processo la stringa da mandare e c'è un'altra verifica di correttezza sulla stringa. 
Ultima cosa è che potreste chiudere la calcolatrice usando PostMessage con WM_QUIT (non ho provato WM_CLOSE o la PostQuitMessage).
 
2) SECONDA FASE: LA CALCOLATRICE COMUNICA CON NOI
In questa fase vi spiego come posso aggiungere una funzione alla calcolatrice e far in modo che un tasto mandi la stringa che sta sulla calcolatrice in una EditBox del mio programma.
Il metodo che ho scelto è quello di DEFINIRE un USER MESSAGE nuovo e unico nel sistema, cioè per la comunicazione mi appoggio a un messaggio che definisco io e che non è uno dei classici WM_COMMAND, WM_SETTEXT...; non posso usare WM_COMAND perche nel mio proggy non c'è un tastierino numerico ovviamente. 
Vediamo quindi le potenzialità dell'API RegisterWindowMessage
 
La RegisterWindowMessage function definisce un nuovo window message che ha la garanzia di essere unico in tutto il sistema. Il valore di ritorno della funzione può essere usato quando si chiama SendMessage o PostMessage.

UINT RegisterWindowMessage(
LPCTSTR lpString     // address of message string
);   
Parameters

lpString
Points to a null-terminated string that specifies the message to be registered.

Return Values
Se il messaggio è registrato corettamente, il valore di ritorno è un identificatore del messaggio in questo range 0xC000 to 0xFFFF.
If the function fails, the return value is zero.

Remarks
La RegisterWindowMessage function è tipicamente usata per registrare messaggi di communicazione tra  due applicazioni cooperanti.
Se due differenti applicationi registrano la stessa message string, le applicazioni ritornano lo stesso message value. Il messaggio rimane registrato fino alla fine della Windows session.

A parte la mia orribile traduzione... posso definire un nuovo identificatore per un messaggio attraverso la API RegisterWindowMessage e definendo una Stringa che dovrà ovviamente essere identica sia nella registrazione nel mio programma che nella registrazione nella calcolatrice.
 
REGISTRAZIONE DEL MESSAGGIO NEL MIO PROGGY
Definisco la seguente stringa WM_CALCEXT che poi identificherà il messaggio
 
MessageIdentifier=RegisterWindowMessage("WM_CALCEXT");//MessageIdentifier sarà il parametro che definisce il tipo di messaggio nelle SendMessage
 
Questa istruzione va eseguita una sola volta a inizio di programma e nel codice sorgente la trovate dentro la funzione eseguita quando viene processato il WM_INITDIALOG che è uno dei primi messaggi che win manda a un'applicazione che deve essere caricata.
 
GESTIONE DA PARTE DEL PROGGY DEI WM_CALCEXT CHE GLI MANDERA' LA CALCOLATRICE
Facciamo l'assunzione che la calcolatrice sia in grado di mandare dei WM_CALCEXT al proggy (vedremo come farlo dopo) e il significato dei WParam e LParam associati ai messaggi che arrivano al proggy.
Vediamo subito il codice che è già esplicativo.
 
static BOOL CALLBACK DialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (msg==MessageIdentifier) {
                                    if (lParam!=',')
                                            if (lParam!='X') RegRecived[wParam]=lParam;
                                            else {
                                                    SetDlgItemText(hwndDlg,IDREC,RegRecived);
                                                   }
                                    else {
                                            RegRecived[wParam]='\0';
                                            MessageBoxA(hwndDlg,"Mandata stringa non esadecimale","Errore",MB_OK);
                                        }
                                }
 
Vi ho riportato anche l'Header della funzione che processa i messaggi che arrivano al proggy, si vede subito quello che succede: verifico che il messaggio che arriva sia uguale a quello che ho registrato io, LParam del messggio è il valore che corrisponde al carattere ASCII che voglio che venga ricevuto.
Sta succedendo questo la calcolatrice manda tanti WM_CALCEXT quanti sono i caratteri della stringa che voglio trasmettere, e LParam porta un valore che equivale a un carattere conforme alla codifica ESADECIMALE mentre WParam definisce l'ordine in una buffer di memorizzazzione (RegRecived) dei valori che arrivano.Facciamo un esempio: 
la calcolatrice vuole mandare la stringa A3E; manderà i seguenti messaggi (poi vi farò vedere come si fà)
msg=WM_CALCEXT, LParam=41h (A), WParam=0 così RegRecived[WParam]=LParam significa RegRecived[0]=A
msg=WM_CALCEXT, LParam=33h (3), WParam=1 così RegRecived[WParam]=LParam significa RegRecived[1]=3
msg=WM_CALCEXT, LParam=45h (E), WParam=2 così RegRecived[WParam]=LParam significa RegRecived[2]=E
Per finire la stringa la calcolatrice manda un
msg=WM_CALCEXT, LParam=0h (\0), WParam=3 così RegRecived[WParam]=LParam significa RegRecived[3]=\0 cioè il terminatore
msg=WM_CALCEXT, LParam=58h (X), WParam=0 che serve solo ad avvertire il mio proggy che la stringa è completa e si puo chiamare SetDlgItemText per settare la stringa nella EditBox opportuna
Questo è il meccanismo di comunicazione che ho scelto e così dovrà essere implementato nella calcolatrice.
Ancora due parole sui check effettuati: a questo livello non c'è nessuna verifica sul fatto che la stringa sia effettivamente esadecimale perchè si suppone che la calcolatrice di Win non sia in grado di mandare stringhe che non abbiano significato numerico, viene invece effettuato un check sull'invio della "virgola" perchè uno potrebbe sbagliarsi e mandare una stringa decimale, se viene intercettata una virgola si viene avvertiti dell'errore e viene stampata solo la parte della stringa arrivata prima della vigola, manca un check sul fatto che WParam non possa superare 7 (0...7 sono 8 char) cioè che non venga spedita una stringa superiore a quella che rappresenta una DWORD. 
 
3) TERZA FASE: DEFINIZIONE E PREPARAZIONE "DI UN TERRENO LAVORABILE" E REALIZZAZZIONE DEL PATCHING DI WINCALC
Prima cosa da fare è definire quello che si deve fare e come lo si vuole fare:
Cosa si deve fare:
1) Trovare lo spazio per inserire il nostro codice
2) Trovare un tasto "inutile" nella calcolatrice da sfruttare per agganciarci la nostra funzione che spedisce la stringa
3) Trovare i punti in cui agganciare questa funzioni
4) Introdurre quindi due funzioni una per eseguire la RegisterWindowMessage (4a) e l'altra per sendare i messaggi (4b)
 
 
Come lo facciamo?
PUNTO 1
Risolviamo subito il punto 1 utilizzando questo approccio: introdurremo una nuova sezione nell'eseguibile della calcolatrice con il SADD.Io l'ho chiamata .CExt con dimensione 5000h (lo so che è esagerata ma chi caz se ne frega...).
In questa nuova sezione metteremo sia le due funzioni che introdurremo che i dati che ci serviranno all'interno della funzioni.
Abbastanza facile no???
 
PUNTO 2
Anche il punto due è abbastanza banale vi faccio l'elenco dei tasti inutili della calcolatrice LSH (che è buggato e c'è già nel mio proggy) X^2 e X^3 (dato che c'è X^Y) e soprattutto l'istruzione INT (che vi da la parte intera di un numero e la sua utilità è "zero")
La scelta cade sul tasto INT perchè se lavoriamo nel formato esadecimale ce ne sbattiamo della parte intera che "non esiste".
 
PUNTO 3
Forse quello più interessante perchè andiamo finalmente a disassemblare Calc.exe (la calcolatrice di Win ovvio;))) con IDA e ci mettiamo a guardare il codice.
Andiamo alla ricerca di un punto dove inserire un JUMP che ci porti alla sezione che abbiamo introdotto dove faremo eseguire la nostra routine di registrazione del messaggio WM_CALCEXT. Dobbiamo tener presente che prima lo facciamo meglio è quindi l'ideale è trovare una cave dove iserire il JUMP nella fase di caricamento delle risorse. Andiamo quindi a vedere la call iniziale chiamata Start che inizia a :10119E0 (non è il solito :400000...)
e io trovato questo punto:

01011B24 push eax     
01011B25 push esi
01011B26 push 0
01011B28 push 0
01011B2A call ds:GetModuleHandleA
Possiamo introdurre un bel JUMP in questo punto al posto delle 4 push che poi però dovremmo ripristinare subito dopo il jump.
Il risultato sarà questo una volta definito il punto di arrivo

01011B24 jmp loc_1017018 ;ovviamente questo è il punto in cui inizierà la funzione di registrazione
01011B29 nop ;per aggiustare il tutto
01011B2A
01011B2A loc_1011B2A: ; CODE XREF: .CExt:01017031j
01011B2A call ds:GetModuleHandleA

Vi faccio notare che il jump non porta all'inizio della sezione che abbbiamo creato noi (che parte da :1017000) perchè dobbiamo riservare un po di spazio per i dati che ci serviranno nella funzione (vedremo dopo come calcolare questo spazio). Altra cosa per patchare ho sempre usato Hiew e l'OPGEN per avere gli opcode dei jump da effettuare.
 
Ora dobbiamo andare a cercare il secondo punto dove metteremo il JUMP per la seconda funzione; qui siamo vincolati alla scelta del tasto INT (della calcolatrice) e dobbiamo per forza trovare il punto in cui questo tasto viene processato dopo essere stato premuto.
Io ho ragionato così: ogni tasto che viene premuto comporta una modifica nella EditBox della calcolatrice di Win quindi cercheremo di brekkare su qualche API che va a modificare la stringa. Per velocizzare il tutto diamo un'occhaita nella con IDA alla IMPORT TABLE della calcolatrice di WIN e dovreste vedere che l'API più probabile per il settaggio della stringa nella EditBox è SetDlgItemTextA quindi non ci resta che fare un bel BPX su quest'API.
 
Ora premiamo un tasto numerico e vediamo che succede. Atterriamo qui vi riporto il codice:

01004177 push 193h
0100417C push dword_1013D6C
01004182 call ds:SetDlgItemTextA

01004188 loc_1004188: ; CODE XREF: sub_1003F05+8Cj //Atterriamo qui
01004188 push ebx
01004189 call sub_1002280
0100418E pop edi
0100418F pop esi
01004190 pop ebx
01004191 retn
01004191 sub_1003F05 endp

Da notare subito che il testo viene settato con l'esecuzione della SetDlgItemTextA quindi il tasto è già stato processato e la WindowProcedure ha già stabilito che cosa fare e probabilmente si stà preparando a rientrare nel LOOP dei acquisizione dei messaggi, ma c'è da notare un'altra cosa, provate ora dopo aver scritto un numero a premere X^2 (il quadrato del numero) o X^3 e noterete che stavolta la SetDlgItemTextA non setta la stringa rappresentante il quadrato del numero ma questo avviene più avanti, ora se avete provato prima con Exescope a cercare i wID dei tasti dovreste notare quale interessantissimo valore finisce in ESI con il POP in :100418F, cioè proprio il wID del tasto che avete schiacciato.
Sembra fatto apposta per noi questo pezzo di codice, consideriamo quindi tutti i punti: abbiamo un punto del codice dove riusciamo a individuare con esattezza il tasto premuto quindi anche quando è premuto INT, l'update della stringa nella EditBox è fatta più avanti qunado è premuto il tasto INT che non è uno di quelli numerici (ma si comporta come X^2), se spediamo la stringa da questo punto quando ritorniamo dalla nostra funzione possiamo riprendere l'esecuzione esattamente da qui perchè comunque l'INT di un numero esadecimale è uguale a se stesso.
 
Mettiamo quindi un bel JUMP a un punto successivo della sezione che abbiamo introdotto e il codice diventa così:
0100417C push DialogBoxHandle
01004182 call ds:SetDlgItemTextA
01004188
01004188 loc_1004188: ; CODE XREF: sub_1003F05+8Cj
01004188 jmp loc_10170A0
0100418D ; ---------------------------------------------------------------------------
0100418D nop
0100418E
0100418E loc_100418E: ; CODE XREF: .CExt:010170ADj
0100418E ; .CExt:010170E5j ...; ritorno qui dopo la mia funzione
0100418E pop edi
0100418F pop esi
01004190 pop ebx
01004191 retn
Ovviamente abbiamo tolto del codice che dovremo reinserire poi. Il jump come al solito tiene in considerazione di un po di spazio prima della seconda funzione che mi serve per le variabili che dovrò utilizzare.
 
PUNTO 4
Non ci resta che scrivere le funzioni.
4a) La Prima Funzione: Registriamo WM_CALCEXT
La prima cosa da fare è definire quanto spazio occupano le costanti e le variabili che mi servono nella funzione.
In ordine:
1 DWORD per l'identificatore del messaggio restituito da RegisterWindowMessage
11 BYTE per la stringa WM_CALCEXT,0 (terminatore) approssimo a 3 DWORD
1 DWORD per l'address della funzione RegisterWindowMessage
1 DWORD di separazione dalla funzione
 
Il risultato dopo la patchata è questo (lo prendo dal disassemblato di IDA dopo che ho finito):
01017000 ; Segment type: Regular
01017000 _CExt segment para public '' use32
01017000 assume cs:_CExt
01017000 ;org 1017000h

01017000 assume es:nothing, ss:nothing, ds:_data, fs:nothing, gs:nothing
01017000 MessageIdentifier dd 0 ; DATA XREF: .CExt:0101702Cw
01017000 ; .CExt:010170FEr ...
01017004 aWm_calcext db 'WM_CALCEXT',0 ; DATA XREF: .CExt:01017020o
0101700F db 0 ;
01017010 AddressRegWindowMessage dd 0BFF514F0h ; DATA XREF: .CExt:01017026r
01017014 dd 0
Seguendo l'ordine sopra e partendo dall'inizio della sezione che ho introdotto .CExt ho assegnato la prima DWORD (le label le ho messe io dopo ovviamente) che è ovviamente a 0 perchè gli sarà assegnato il valore a RunTime da RegWindowMessage; la stringa WM_CALCEXT la scrivete a partire dalla seconda DWORD della sezione mettete il terminatore 00 e lasciate un BYTE a 0 per l'allineamento a DWORD (in teoria potreste allinearvela come vi pare ma io faccio così perchè mi sembra più ordinato).
Un discorso a parte per ciò che dovete mettere nell'ADDRESS della RegisterWindowMessage: io ho scelto una strada che purtroppo richiede un po di buona volontà da parte vostra. Mi spiego la RegisterWindowMessage è una funzione della USER32.DLL che è una delle DLL caricate dalla calcolatrice ma non è una funzione della IT della calcolatrice stessa: quindi non ho una locazione della IT con l'address quindi non posso fare direttamente una Call dword ptr [ADDRESS] ma devo ottenere prima questo address. I metodi scelti sono diversi: dalla modificazione della IT, all'utilizzo di GetProcAddress, alla strada che ho scelto io: cioè ho preso Exescope ho aperto USER32.DLL ho cercato l'address della funzione ReisterWwindowMessasge è l'ho cacciato dentro come se fosse una costante. Questo comporta un problema perchè se avete una versione di USER32.DLL diversa dalla mia gli address saranno diversi è quindi il programma genera un GPF. La soluzione sta nel fatto che vi pigliate l'Exescope e cambiate l'address con quello corretto della vostra USER32.DLL. Lo so gli altri due metodi non avrebbero comportato problemi ma non avevo voglia... 
 
E ora la prima funzione, vi iporto il codice:
01017018
01017018 loc_1017018: ; CODE XREF: start+144j
01017018 push eax ; metto a posto il codice che avevo sostituito con il jump
01017019 push esi
0101701A push 0
0101701C push 0
0101701E nop
0101701F nop ; inizia la mia routine di registrazione
0101701F ; del messaggio che userò per comunicare
01017020 push offset aWm_calcext ; "WM_CALCEXT" ;unico parametro di RegWindowMessage
01017025 nop
01017026 call ds:AddressRegWindowMessage ; in Hiew quando patchate dovete mettere
                                      ;   call d,[1017010] ovviamente
0101702C mov ds:MessageIdentifier, eax  ; in Hiew dovete mettere mov [1017000],eax
01017031 jmp loc_1011B2A  ; fine routine

Ora si capisce anche che il punto da e da dove arriva il salto è definito in base allo spazio che mi serve per le costanti e le variabili.
 
4b) La Seconda Funzione: Mandiamo la stringa
Il ragionamento che facciamo è uguale al precedente (GPF compersi)...
Vi riporto lo spazio per i dati:

01017037 db 0 ; per l'allineamento
01017038 HandleCalcExtender dd 0 ; DATA XREF: .CExt:010170C3w   ;Handle al mio prog
01017038 ; .CExt:01017104r ...
0101703C dd 0BFF51743h ;address GetDlgItemMessage;inutile mi serviva per un'altra cosa...
01017040 AddressFindWindow dd 0BFF55918h ; DATA XREF: .CExt:010170BDr
01017040 ; address FoundWindow stesso discorso di RegisterWindowMessage
01017044 a32770 db '#32770',0 ; DATA XREF: .CExt:010170B8o ; Class name del mio proggy
0101704B db 0 ; allineamento
0101704C aCalcextender1_0 db 'CalcExtender 1.0',0 ; DATA XREF: .CExt:010170B3o
                                                    ; window title
0101705D db 0 ;
0101705E db 0 ;
0101705F db 0 ;
01017060 aCalcextenderNonCaricat db 'CalcExtender non caricato',0
01017060 ; DATA XREF: .CExt:010170D4o ; error message
0101707A db 0 ;
0101707B db 0 ;
0101707C aError_0 db 'ERROR',0 ; DATA XREF: .CExt:010170CFo ; error caption
01017082 db 0 ;
...    ; inutili mi servivano per altre cose
0101709F db 0 ;
Dato che devo spedire dei messaggi mi servono l'handle al mio proggy che trovo con FindWindow, i parametri di FindWindow cioè Class Name (che a differenza dell'utilizzo fatto all'inizio non basta più perchè almeno sul mio PC questo ClassName identifica anche altre cose) e il Window Title, più caption e message per una MBoxA che avverte se CalcExtender non è caricato.
Eccovi la funzione:

010170A0 loc_10170A0: ; CODE XREF: sub_1003F05+283j
010170A0 push ebx           ;codice sostituito dal jump
010170A1 call sub_1002280    ;codice sostituito dal jump
010170A6 mov eax, [esp+4]; in [esp+4] c'è quel valore che finirà in esi cioè il wID del                              tasto premuto di volta in volta
010170AA cmp eax, 60h    ; verifico se il tasto premuto è INT
010170AD jnz loc_100418E ; se non è INT torno a dove ero prima e proseguo nella normale                              esecuzione
010170B3 push offset aCalcextender1_0 ; window title
010170B8 push offset a32770 ; Class name
010170BD call ds:AddressFoundWindow ; Call FindWindow
010170C3 mov ds:HandleCalcExtender, eax ; handle CalcExtender
010170C8 cmp eax, 0
010170CB jnz short loc_10170EA   ;Se handle=0 Chiamo la MBoxA di errore altrimenti vado a                      spedire il messaggio
010170CD push 0
010170CF push offset aError_0 ; error caption
010170D4 push offset aCalcextenderNonCaricat ; error message
010170D9 push DialogBoxHandle   ; Handle calcolatrice di Win
010170DF call ds:MessageBoxA
010170E5 jmp loc_100418E   ; esco e torno a escuzione normale
010170EA ; ---------------------------------------------------------------------------
010170EA
010170EA loc_10170EA: ; CODE XREF: .CExt:010170CBj
010170EA xor ebx, ebx    ;uso ebx come contatore e indicherà la posizione del carattere                      nella stringa
010170EC mov esi, offset dword_1013DA0 ; la beccate dalla SetDlgItemTextA che setta la stringa
010170F1 mov edi, [esi]
010170F3
010170F3 loc_10170F3: ; CODE XREF: .CExt:01017113j
010170F3 cmp byte ptr [edi], 0  ; in edi di volta in volta c'è il caratere che voglio              spedire e ciclo fino a che non trovo il terminatore
010170F6 jz short loc_1017118
010170F8 xor edx, edx
010170FA mov dl, [edi] ;in edx ci sarà la DWORD di LPARAM
010170FC push edx
010170FD push ebx
010170FE push ds:MessageIdentifier
01017104 push ds:HandleCalcExtender ; handle CalcExtender
0101710A call ds:SendMessageA       ;spedisco il messaggio
01017110 inc edi    ; vado al char successivo
01017111 inc esi        ;è inutile ma non fa danno (mi è rimasto mentre debuggavo)
01017112 inc ebx    ; incremento il contatore
01017113 jmp loc_10170F3
01017118 ; ---------------------------------------------------------------------------
01017118
01017118 loc_1017118: ; CODE XREF: .CExt:010170F6j
01017118 nop        ;arrivo quando trovo il terminatore della stringa
01017119 push 0     ;spedisco il terminatore
0101711B push ebx
0101711C push ds:MessageIdentifier
01017122 push ds:HandleCalcExtender ; handle CalcExtender
01017128 call ds:SendMessageA
0101712E push 58h        ;spedisco il 'X'
01017130 push 0
01017132 push ds:MessageIdentifier
01017138 push ds:HandleCalcExtender ; handle CalcExtender
0101713E call ds:SendMessageA
01017144 jmp loc_100418E     ; riprendo l'esecuzione normale
Il codice è abbastanza chiaro logicamente dovete sapere usare Hiew per scrivere la routine, usare OPGEN per una verifica sugli OPCODE dei salti, e un po di pazienza.
La prassi di comunicazione utilizzata è proprio quella che avevamo definito nella seconda fase quando abbiamo definito l'ordine di arrivo dei messaggi dalla calcolatrice al proggy.
Rimane da chiarire ancora un aspetto cioè dove trovo: l'Handle della calcolatrice che uso per la MBoxA di errore, l'Addres di SendMessageA e MBoxA, la stringa da spedire.Tutti questi sono dati che abbiamo già a disposizione perchè: la DWORD dove è messo l'handle è usata in altri punti per esempio quando uso SetDlgItemTextA; MBoxA e SendMessage sono API che sono IMPORTATE nella IT quindi ho già l'address delle funzioni, per finire la stringa è proprio quella che viene pushata in SetDlgItemTextA nel punto in cui poi io ho messo il jump, quindi la stringa che processo è quella che sono sicuro di avere nella EditBox.
 
FINIAMO CON I PROBLEMI NOTI CHE POTRESTE INCONTRARE
1) La calcolatrice di WIN patchata che trovate nel pacco non funziona se non avete la USER32.DLL come la mia: soluzione cambiate l'address della RegisterWindowMessage e della FindWindow in maniera coerente alla DLL che avete voi
2) CalcExtender comunica con le versioni della calcolatrice di Win che hanno i wID dei tasti della calcolatrice di Win col valore ugale a quello della calcolatrice che ho io: soluzione vi do il codice di CalcExtender e se avete i wID diversi li cambiate e ricompilate
3) La calcolatrice di Win95 è diversa da quella di Win98 (nel senso che quella di Win95 ha meno Bugs e non scherzo) e quindi le righe di codice non corrispondono tra le due versione ergo vi dovete trovare altri punti per inserire i JUMP alle funzioni da inserire:soluzione vi fate un vostro reversing...
 
Con questo dovrei aver concluso .
 
Byez Guzura

Note finali

Questo lavoro mi ha soddisfatto ed è stato possibile grazie ai tutorial su vari argomenti da cui ho preso spunto perchè alla fine tutto si riduce all'applicazione di argomenti trattati da altri: Ringraziamo Que e Fyste per i loro tutorial sul Memory Patching, Kill3x per i suoi tutorial sul PE, Iczelion per i suoi tut sull'asm, Jacob Navia per il suo fantastico tut sulla programmazione C per Win32, GEnius per il suo tut sul Code injection, Ritz per il suo tut sul PMailbox e tutti gli amici di #Crack-it e della ML che mi danno consigli su tutto.

Disclaimer

Vorrei ricordare un amico che pochi giorni fà è scomparso. Ciao Roberto.