lunedì 19 settembre 2011

Buffer di dati per il visual basic che li leggerà come codice macchina

avendo avuto la febbre giorni fa che mi ha costretto a stare a casa circa una settimana ho sfruttato il tempo tra le altre cose di modficare alcuni programmi, alcuni sperimentali di famosi informatici americani (alcuni non so che fine hanno fatto).
un algo che vorrei mettere è questo qui fatto in visual basic 6 (a titolo dimostrativo, meglio programmare in c/c++ o visual .net).
siccome il vb non permette di creare assembly inline come fa il C c'è un trucco ed è usato o meglio era usato spesso da alcuni genietti per superare le limitazioni ovvie del vb.
questo algo non fa altro che iniettare in memoria (in modo regolare) un buffer che contiene dati e poi viene passato alla funzione CallWindowProc() quella usata per subclassare i messaggi win, tanto per intenderci, e esso non fa altro che leggere il buffer di dati come sequenza di codice macchina
per chi l'ha capito il trucco è questo: i dati vengono letti come normali opcode, basta bassare alla funzione CallWindowProc() il puntatore al buffer dei dati, tra le altre cose nel buffer si possono mettere anche indirizzi di variabili stringhe (per esempio se si vuole usare una semplice MessageBoxA() o come in questo caso metteremo indirizzi di variabili perche vorremo salvarci i valori che avremo dopo aver eseguito il processo.
allora ricapitolando buffer di dati, chiamata alla funzione CallWindowProc() a cui passeremo come primo parametro il puntatore al buffer (questo buffer fungerà da una normale funzione) e valori di ritorno.
la funzione interna è, per chi conosce l'assembly, il mnemonico CPUID che in esadecimale sarebbe 0FA2, e prima della chiamataa CPUID pusheremo tutti i registri importanti altrimenti (logicamente) l'interprete del vb crasha, e poi li popperemo, inoltre cosa importante calcoleremo l'address delle variabili di ritorno e li trasformeremo anch'esse in esadecimale facendo attenzione di invertire i byte perchè
la cpu che quasi tutti abbiamo (specie perchè gira il vb) è little endian cioe la memoria viene letta dalla cpu da destra a sinistra (la cpu motorola del mac legge normale da sinistra a destra come fanno i nostri occhi, nel gergo informatico big endian)
questa iniezione di dati letti come codice tra l'altro fu usato da due genietti dell'informatica Paul Caton e Vlad Vissoultchev che lo usarono per creare uno sniffer (che io ho modificato qualche giorno fa) fatto completamente in visual basic, il cui codice veniva modificato a run-time perchè deve chiamare le socket e altre funzioni api che non sono logicamente hardcodate (ogni service pack e sistema operativo ha i suoi indirizzi di memoria)
detto questo ecco l'algo per leggere la versione della cpu (per chi non lo sapesse dopo aver chiamato l'opcode cpuid la cpu salva i dati (ascii) in sequenza nei registri ebx + edx + ecx:
 un ultima cosa, chi mastica bene o male queste cose e vuole crearsi le proprie funzioni in assembly, gli conviene crearsi
i propri opcode direttamente sul debuger ollydbg, oppure crearsi il codice in asm e poi copiarlo da ollydbg, oppure crearselo direttamente in C e poi copiarlo sempre da ollydbg, con l'accortezza però di non usare chiamate alle funzioni run-time del C ma crearseli da se, perche quanto poi diventano opcode per il VB, le chiamate alle funzioni run-time del C non hanno senso, nel contesto del VB

Sub Main()
    InjectionCodeAsmAscii_CPUID
End Sub

Sub InjectionCodeAsmAscii_CPUID()
Dim Asm As String
Dim eax As Long
Dim ebx As Long
Dim ecx As Long
Dim edx As Long

'*****COMMENTO: questo è il codice macchina che ho creato a mano con ollydbg gli indirizzi 14567C sono fittizi li ho paddati per fare l'allineamento, gli indirizzi effettivi li calcolo a run-time nel Vb stesso***************
'0014E9D6 55 PUSH EBP
'0014E9D7 8BEC MOV EBP,ESP
'0014E9D9 50 PUSH EAX
'0014E9DA 53 PUSH EBX
'0014E9DB 51 PUSH ECX
'0014E9DC 52 PUSH EDX
'0014E9DD 33C0 XOR EAX,EAX
'0014EAD5 0FA2 CPUID
'0014E9DF A3 78561400 MOV [14567C],EAX
'0014E9E4 891D 7C561400 MOV [14567C],EBX
'0014E9E4 890D 7C561400 MOV [14567C],ECX
'0014E9EA 8915 7C561400 MOV [14567C],EDX
'0014E9F0 5A POP EDX
'0014E9F1 59 POP ECX
'0014E9F2 5B POP EBX
'0014E9F3 58 POP EAX
'0014E9F4 C9 LEAVE
'0014E9F5 C2 1000 RETN 10
'*********FINE COMMENTO *************

Asm = Asm & Chr(&H55)
Asm = Asm & Chr(&H8B) & Chr(&HEC)
Asm = Asm & Chr(&H50) & Chr(&H53) & Chr(&H51) & Chr(&H52)
Asm = Asm & Chr(&H33) & Chr(&HC0)
Asm = Asm & Chr(&HF) & Chr(&HA2)
Dim StructForEax As TypeAddr
Dim addrForEax As Long

Dim StructForEbx As TypeAddr
Dim addrForEbx As Long

Dim StructForEcx As TypeAddr
Dim addrForEcx As Long

Dim StructForEdx As TypeAddr
Dim addrForEdx As Long

StructForEax = restituisciByteLong(VarPtr(addrForEax))
StructForEbx = restituisciByteLong(VarPtr(addrForEbx))
StructForEcx = restituisciByteLong(VarPtr(addrForEcx))
StructForEdx = restituisciByteLong(VarPtr(addrForEdx))

Asm = Asm & Chr(&HA3) & Chr(StructForEax.addr1) & Chr(StructForEax.addr2) & Chr(StructForEax.addr3) & Chr(StructForEax.addr4)
Asm = Asm & Chr(&H89) & Chr(&H1D) & Chr(StructForEbx.addr1) & Chr(StructForEbx.addr2) & Chr(StructForEbx.addr3) & Chr(StructForEbx.addr4)
Asm = Asm & Chr(&H89) & Chr(&HD) & Chr(StructForEcx.addr1) & Chr(StructForEcx.addr2) & Chr(StructForEcx.addr3) & Chr(StructForEcx.addr4)
Asm = Asm & Chr(&H89) & Chr(&H15) & Chr(StructForEdx.addr1) & Chr(StructForEdx.addr2) & Chr(StructForEdx.addr3) & Chr(StructForEdx.addr4)

Asm = Asm & Chr(&H5A) & Chr(&H59) & Chr(&H5B) & Chr(&H58)
Asm = Asm & Chr(&HC9)
Asm = Asm & Chr(&HC2) & Chr(&H10) & Chr(&H0)



ret = CallWindowProc(Asm, 0, 0, 0, 0)

'in addrForEax, cioe eax,ci sta la lunghezza ritornata dalla stringa CPUID

For a = 7 To 1 Step -2
ascii = ascii & Chr(Val("&H" & Mid(Hex(addrForEbx), a, 2)))
Next a
For a = 7 To 1 Step -2
ascii = ascii & Chr(Val("&H" & Mid(Hex(addrForEdx), a, 2)))
Next a
For a = 7 To 1 Step -2
ascii = ascii & Chr(Val("&H" & Mid(Hex(addrForEcx), a, 2)))
Next a
MsgBox (ascii)
End Sub


Private Type TypeAddr
   addr1 As Byte
   addr2 As Byte
   addr3 As Byte
addr4 As Byte 'addr4 addr3 addr2 addr1 --- dal meno significativo (dx) al piu significativo (sx)
End Type

'restituisciByteLong come si può ben vedere inverte i byte degli address di memoria che poi inseriremo byte per byte assieme agli altri opcode
Function restituisciByteLong(ByVal ptrLong As Long) As TypeAddr
    restituisciByteLong.addr4 = (ptrLong And &HFF00) \ 2 ^ 24
    restituisciByteLong.addr3 = ((ptrLong And &HFF00) \ 2 ^ 16) And &HFF
    restituisciByteLong.addr2 = ((ptrLong And &HFF00) \ 2 ^ 8) And &HFF
    restituisciByteLong.addr1 = ptrLong And &HFF
End Function

Nessun commento:

Posta un commento