Soyez le premier à donner votre avis sur cette source.
Vue 5 009 fois - Téléchargée 577 fois
'# BruNews, s'il te vient l'idée de lire ce code ASM, '# rappelle toi que je suis pas spécialiste, hein ^^ Private Sub Class_Initialize() Dim pFirstVTEntry As Long Dim pCode As Long Dim i As Long GetMem4 ObjPtr(Me), pFirstVTEntry mpCode = VirtualAlloc(ByVal 0&, 512, MEM_COMMIT, PAGE_EXECUTE_READWRITE) '# Invoke et InvokeArr pCode = mpCode For i = 0 To 1 PutMem4 pFirstVTEntry + &H1C& + i * 4, pCode PutMem4 pCode, &H56EC8B55: pCode = pCode + 4 'push ebp 'mov ebp, esp 'push esi PutMem4 pCode, &H9010758B: pCode = pCode + 4 'mov esi, [ebp+10h] If i Then '# InvokeArr PutMem4 pCode, &H9008768D: pCode = pCode + 4 'lea esi, [esi+8h] => Permet de sauter l'entete du Variant (type+2 reserved words) End If PutMem4 pCode, &H488B068B: pCode = pCode + 4 'mov eax, [esi] 'mov ecx, [eax+10h] PutMem4 pCode, &H14483B10: pCode = pCode + 4 'cmp ecx, [eax+14h] PutMem4 pCode, &H9090267E: pCode = pCode + 4 'jle LBL_CALL (36+2) PutMem4 pCode, &HEA83D18B: pCode = pCode + 4 'mov edx, ecx PutMem4 pCode, &H4E2C101: pCode = pCode + 4 'sub edx, 1 'shl edx, 4 'LBL_NEXT_ARG PutMem4 pCode, &H30C408B: pCode = pCode + 4 'mov eax, [eax+0Ch] PutMem4 pCode, &H8C083C2: pCode = pCode + 4 'add eax, edx PutMem4 pCode, &H68B30FF: pCode = pCode + 4 'push [eax] PutMem4 pCode, &H9001E983: pCode = pCode + 4 'mov eax, [esi] 'sub ecx, 1 PutMem4 pCode, &H3B10EA83: pCode = pCode + 4 'sub edx, 10h PutMem4 pCode, &H90901448: pCode = pCode + 4 'cmp ecx, [eax+14h] PutMem4 pCode, &H9090E67F: pCode = pCode + 4 'jg LBL_NEXT_ARG (-24-2) 'LBL_CALL PutMem4 pCode, &H8B0C55FF: pCode = pCode + 4 'call [ebp+0Ch] PutMem4 pCode, &H2891455: pCode = pCode + 4 'mov edx, [ebp+14h] 'mov [edx], eax PutMem4 pCode, &H5D5EC033: pCode = pCode + 4 'xor eax, eax 'pop esi 'pop ebp PutMem1 pCode, &HC3: pCode = pCode + 4 'ret Next '# Invoke 0-4 For i = 0 To 4 PutMem4 pFirstVTEntry + &H24& + 4 * i, pCode PutMem4 pCode, &H90EC8B55: pCode = pCode + 4 'push ebp 'mov ebp, esp If i = 4 Then PutMem4 pCode, &H901C458B: pCode = pCode + 4 'mov eax, [ebp+1Ch] PutMem4 pCode, &H9018558B: pCode = pCode + 4 'mov edx, [ebp+18h] PutMem4 pCode, &H90144D8B: pCode = pCode + 4 'mov ecx, [ebp+14h] PutMem4 pCode, &H90515250: pCode = pCode + 4 'push eax 'push edx 'push ecx PutMem4 pCode, &H5010458B: pCode = pCode + 4 'mov eax, [ebp+10h] ElseIf i Then If i >= 3 Then PutMem4 pCode, &H9018558B: pCode = pCode + 4 'mov edx, [ebp+18h] End If If i >= 2 Then PutMem4 pCode, &H90144D8B: pCode = pCode + 4 'mov ecx, [ebp+14h] End If PutMem4 pCode, &H9010458B: pCode = pCode + 4 'mov eax, [ebp+10h] If i >= 3 Then PutMem1 pCode, &H52: pCode = pCode + 1 'push edx End If If i >= 2 Then PutMem1 pCode, &H51: pCode = pCode + 1 'push ecx End If PutMem1 pCode, &H50: pCode = pCode + 1 'push eax Do While (pCode Mod 4) PutMem1 pCode, &H90: pCode = pCode + 1 Loop End If PutMem4 pCode, &H900C4D8B: pCode = pCode + 4 'mov ecx, [ebp+0C] PutMem4 pCode, &HD1FF9090: pCode = pCode + 4 'call ecx PutMem4 pCode, &H9000558B + &H10000 * (&H10 + i * 4): pCode = pCode + 4 'mov edx, [ebp+XX] PutMem4 pCode, &H2899090: pCode = pCode + 4 'mov [edx], eax PutMem4 pCode, &HC35DC033: pCode = pCode + 4 'xor eax, eax 'pop ebp 'ret Next End Sub
J'ai besoin de changer le nom de la DLL dynamiquement
Apparement pas possible a faire avec VB6, du fait que DECLARE ne le permet pas :-(
Donc ce code pourrait etre une alternative a mon probleme
Peut on appeller une DLL standard perso, lui envoyer une structure et recevoir un pointeur de structure en retour ??
Un idée de perfectionnement, plutôt qu'Invoke0-4
il serait intéressant d'avoir des fonctions retournant des types autre que long
(Currency , String etc.) 8)
Pour cela, on réserve une plage mémoire de 512 octets qui a l'autorisation de stocker et exécuter du code ; pour éviter que le DEP ne se manifeste.
Quand on considère un objet ActiveX, le pointeur renvoyant vers celui-ci (ObjPtr) est en fait l'adresse vers la VTable de cet objet.
La VTable est un simple tableau de Long. Il s'agit de l'adresse des différentes méthodes de l'objet, dans l'ordre de déclaration de celles-ci ; en prenant en compte IUnknown et IDispatch (qui cumulent 7 méthodes à elles deux).
7 * 4 => 0x1C
La première méthode de notre classe est Invoke.
L'adresse de cette fonction se trouve donc a l'offset 0x1C de la VTable.
Ici:
GetMem4 ObjPtr(Me), pFirstVTEntry
On récupère le début de la VTable.
Et ici:
PutMem4 pFirstVTEntry + &H1C& + i * 4, pCode
On remplace l'adresse correspondante a Invoke puis InvokeArr dans la VTable par l'adresse mémoire voulue ; où on stocke les opCode assembleurs de notre choix.
De façon identique, pour Invoke0-4 :
PutMem4 pFirstVTEntry + &H24& + 4 * i, pCode
Désormais, la VTable de notre objet est hackée. Lorsque nous appellerons nos méthodes, c'est le code assembleur qui sera exécuté.
VB utilise StdCall comme convention d'appel. Dans le cadre de la technologie COM, nous avons droit à quelques subtilités supplémentaires.
Lors de l'appel, on trouve sur la pile :
- L'adresse de l'instruction à exécuter à la sortie de la procédure +0x4
- Le pointeur vers l'objet en cours (Me), automatiquement ajouté par VB +0x8
- Le premier paramètre +0xC
- Le second paramètre
- ...
- Un pointeur vers la variable qui recevra la valeur de retour s'il y a lieu.
Normalement, avec StdCall, le code retour des procédures est transmis via eax. Ici, eax sert à renvoyer le HRESULT. S_OK (0) si tout c'est bien passé, par exemple.
Decryptons un peu l'assembleur. Si on prend le cas de Invoke0 :
push ebp => Sauvegarde de ebp
mov ebp, esp => ebp permettra de 'figer' le pointeur de pile esp
mov ecx, [ebp+0Ch] => ecx contient le 1er param: l'adresse du code à lancer
call ecx => exécution du code assembleur indiqué par l’user
mov edx, [ebp+10h] => edx pointe l’adresse de la valeur de retour
mov [edx], eax => stocke la valeur retournée par la function dans [edx]
xor eax, eax => mise à 0 de eax : HRESULT = S_OK
pop ebp => restauration de ebp
ret => on quitte la procédure
Finalement assez simple, compte tenu de la pile que nous avons pour Invoke0
Si on corse un peu la chose, pour Invoke1 :
push ebp => Sauvegarde de ebp
mov ebp, esp => ebp permettra de 'figer' le pointeur de pile esp
mov eax, [ebp+10h] => eax est 2nd param de Invoke1 : 1er param du code perso
push eax => on place ce parametre sur la pile
mov ecx, [ebp+0Ch] => ecx contient le 1er param: l'adresse du code à lancer
call ecx => exécution du code assembleur indiqué par l’user
mov edx, [ebp+14h] => edx pointe l’adresse de la valeur de retour
mov [edx], eax => stocke la valeur retournée par la function dans [edx]
xor eax, eax => mise à 0 de eax : HRESULT = S_OK
pop ebp => restauration de ebp
ret => on quitte la procédure
Assez peu de changement au final.
Pour Invoke2-4, on reprend les même et on recommence. Gaffe au fait que l’on empile les paramètres du dernier au premier.
Invoke et InvokeArr sont assez similaire (une seule instruction les différencie). Au début de l’appel, leur pile ressemble à cela :
- L'adresse de l'instruction à exécuter à la sortie de la procédure +0x4
- Le pointeur vers l'objet en cours (Me), automatiquement ajouté par VB +0x8
- L’adresse du SafeArray qui stocke nos paramètres dans le cas de Invoke
- L’adresse d’un VARIANT contenant un SafeArray qui stocke nos paramètres dans le cas de InvokeArr
- Un pointeur vers la variable qui recevra la valeur de retour s'il y a lieu.
Assez proches, donc.
push ebp => Sauvegarde de ebp
mov ebp, esp => ebp permettra de 'figer' le pointeur de pile esp
push esi => Sauvegarde esi
mov esi, [ebp+10h] => 1er paramètre du code a lancer
mov eax, [esi] => eax pointe vers mon SafeArray (tableau VB6)
mov ecx, [eax+10h] => ecx est le nombre d’éléments (voir type SAFEARRAY)
cmp ecx, [eax+14h] => comparaison ecx avec l’indice min du tableau
jle LBL_CALL (+38) => pas de paramètre, on saute les 38 octets suivants
mov edx, ecx => edx va etre un offset mémoire pour les paramètres
sub edx, 1 => -1 car on pars de 0
shl edx, 4 => on viens de faire: edx = (ecx-1) * 16
LBL_NEXT_ARG :
mov eax, [eax+0Ch] => eax va pointer vers les données du SafeArray
add eax, edx => on ajoute notre offset, eax pointe le dernier param
push [eax] => on place la valeur sur la pile, pour l’appel
mov eax, [esi] => on replace eax en tête du SafeArray
sub ecx, 1 => on vient de traiter ce paramètre
sub edx, 10h => on décale l’offset pour pointer sur le param précédent
cmp ecx, [eax+14h] => comparaison ecx avec l’indice min du tableau
jg LBL_NEXT_ARG => on boucle tant qu’il reste des paramètres a traiter
LBL_CALL:
call [ebp+0Ch] => on appelle le code perso
mov edx, [ebp+14h] => edx pointe l’adresse de la valeur de retour
mov [edx], eax => stocke la valeur retournée par la function dans [edx]
xor eax, eax => mise à 0 de eax : HRESULT = S_OK
pop ebp => restauration de ebp
ret => on quitte la procédure
pop esi => restauration de esi
pop ebp => restauration de ebp
ret => on quitte la procédure
La seule différence pour InvokeArr se situe dans la récupération de l’adresse du SafeArray (esi).
Pour Invoke, le second parametre est directement notre SafeArray :
mov esi, [ebp+10h]
Pour InvokeArr, nous avons là l’adresse d’un VARIANT de type Tableau de Variant (0x200C). Un variant se décompose ainsi :
- Type (4 octets)
- Donnée réservée 1 (2 octets)
- Donnée réservée 2 (2 octets)
- Donnée du Variant (notre SafeArray)
Du coup, une fois que nous avons en esi l’adresse du variant, un simple :
lea esi, [esi+8h]
Permet de placer en esi l’adresse pointée par esi+8h... notre SafeArray.
Ainsi, la suite du code exploite bien via esi notre SafeArray
Voilà, j’espère que cela vous aidera à bien comprendre ce code, les conventions d’appel et l’assembleur dans une moindre mesure.
C'est vrai que depuis, y'a pas eu trop de rajout de commentaires.
N'empêche je mets quand même 10 parce que tu t'impliques dans les posts de tes lecteurs à un point qu'aucun commentaire ne saura jamais atteindre !
Merci beaucoup pour tout !
(on se recoisera sans doute... je poursuite ma quête: conquérir l'assembleur ! :-) )
le Variant de InvokeArr est maintenant en tant que ByRef. J'ai corrigé le code assembleur de InvokeArr, ca va mieux, pas de crash et code retour bien récupéré...
Vous n'êtes pas encore membre ?
inscrivez-vous, c'est gratuit et ça prend moins d'une minute !
Les membres obtiennent plus de réponses que les utilisateurs anonymes.
Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.
Le fait d'être membre vous permet d'avoir des options supplémentaires.