VBReFormer a vingt ans. Sa première version décompilait déjà du Visual Basic 6 natif à une époque où l'on nous expliquait que « le natif VB6, ça ne se décompile pas ». Depuis, chaque binaire étrange, chaque idiome de compilateur, chaque bizarrerie de MSVBVM60 a fini par entrer dans le produit.
V7 garde cette connaissance et jette tout le reste.
Ce moteur réécrit est aussi celui du décompilateur VB6 en ligne — le même code, dans le navigateur, sans rien installer.
Le moteur est réécrit intégralement, en .NET, à partir de zéro. Pas un portage : une réécriture, où seule la sémantique — dix ans d'observation du runtime VB6 — a été conservée. Et pour la première fois, le natif et le P-code sont traités par le même produit, au même niveau de finition.
Ce qui change vraiment
1. Le natif, enfin traité comme un vrai problème de décompilation
L'ancien moteur travaillait par reconnaissance de motifs : il savait à quoi ressemble le code que génère VB6, et il le rendait. Cela marche magnifiquement — jusqu'au binaire compilé avec un profil d'optimisation inhabituel.
V7 procède autrement. Il interprète abstraitement le code machine : registres, mémoire virtuelle, pile FPU, avec double suivi de la valeur littérale et de l'expression qui l'a produite. Le désassemblage passe par Iced. Le résultat est un arbre d'expressions, pas une chaîne reconnue.
La différence se voit sur les cas difficiles :
- les quatre profils d'optimisation VB6 (Fast Code, Favor Small Code, No Optimization, Remove Safety Checks) sont couverts et mesurés séparément — et, contre-intuitivement, c'est No Optimization qui est le plus dur
- les tables de saut d'un
Select Casesont suivies, y compris quand le corps d'unCasen'a pas d'arête entrante évidente - le modèle Variant est reconstruit (balise, valeur,
ByRef) au lieu d'être deviné - les UDT sur la pile, les tableaux à bornes,
Currency,ParamArraysont reconstruits comme des types, pas comme des octets
2. Le P-code, intégré au même produit
Un binaire VB6 est natif ou P-code, jamais les deux — et jusqu'ici cela voulait dire deux chemins, deux qualités. V7 décompile le P-code de bout en bout : octets → opcodes typés → machine à pile → arbre d'expressions → graphe de flot → structuration → source VB6.
C'est le même étage de structuration qui sert les deux natures. Un Do While … Loop reconstruit depuis du P-code et le même depuis du natif passent par le même code — donc par les mêmes corrections.
3. Les noms
C'est ce qui sépare un décompilateur utilisable d'un vidage d'assembleur. V7 restitue :
- les membres de
Me, les publics, les arguments, les variablesStatic - les appels au runtime
MSVBVM60rendus en idiomes VB (Left(s, 3), pas__vbaStrLeft) — 635 ordinaux répertoriés - les appels virtuels (
VCall), y compris vers les intrinsèques deVB6.OLB New→ le nom de la classe instanciée- l'héritage de formulaire (
Form→_Form) et les accesseurs de contrôle - les bornes et le type d'élément des tableaux, lus des descripteurs de procédure et des IID
Et surtout, V7 dit toujours ce qu'il ne sait pas. Un nom lu du binaire est un fait ; un proc_405630 est un emplacement sans nom. L'inventaire des symboles les compte séparément — sur SETUP1.EXE : 147 noms lus pour 1 182 emplacements synthétisés. Vous savez donc, à chaque ligne, sur quoi vous pouvez vous appuyer.
4. Les types
Un modèle de types complet, avec un treillis, propagé à travers le corps, la signature et les conditions. Les propriétés sont typées par alignement entre Property Get et Property Let. Le type de retour d'un membre de TypeLib est lu de la TypeLib — jamais deviné d'après son nom.
5. Ce que le binaire ne contient pas est écrit
Reference=, les neuf options de compilation, StartMode= : ces informations ne sont dans aucune structure du format VB6. L'ancien produit les déduisait — parfois bien, parfois pas. V7 laisse à leur place une note motivée qui dit pourquoi la valeur manque.
C'est moins joli. C'est vrai.
6. Les tables de référence
Dix ans de rétro-ingénierie de MSVBVM60.DLL et de VB6.EXE, figés dans le produit — aucune n'est dérivée d'un travail tiers :
| Table | Entrées |
|---|---|
| Opcodes de la machine P-code | 1 536 |
| Propriétés de contrôle (31 types) | 1 212 |
| Constantes intrinsèques VB6 | 711 |
Exports du runtime MSVBVM60 | 635 |
| Membres de contrôle | 602 |
| Événements de contrôle | 46 |
Elles ne sont ni lues d'un CSV au démarrage, ni téléchargées : elles sont compilées dans l'assembly, indexées, et interrogées en temps constant.
Les outils
| Graphe d'appels | trois échelles (document, objet, procédure), filtres appelants / appelés, masquage des nœuds isolés, navigation par descente |
| Flot de contrôle | blocs, arêtes, en-têtes de boucle, arêtes arrière, blocs inatteignables en pointillé — et cinq représentations du contenu : forme, IR, VB6, assembleur, P-code |
| Explorateur d'objets et de TypeLib | répond à « où est défini ceci ? » quand la réponse est hors du binaire : membres d'OCX tiers, constantes de VBRUN, signatures reconstruites, bibliothèque nommée |
| Navigateur d'API Win32 | prototypes, alias, notes — pour compléter ce qu'un Declare a perdu |
| Inventaire des symboles | noms lus vs synthétisés, filtrage, renommage global (les sites d'appel des autres modules suivent) |
| Concepteur | la section GUI de chaque formulaire, 1 212 définitions de propriétés de contrôle, 31 types |
| Vue hexadécimale | avec retour « octets → code » |
| Ressources | inventaire, extraction |
| Correctifs binaires | écriture dans l'exécutable analysé |
| Reconstruction de projet | .vbp, .frm, .bas, .cls avec les en-têtes que VB6 exige pour rouvrir |
| Signets, recherche globale, chercheur de programmes et de bibliothèques |
Le banc de mesure
V7 est confronté en permanence à trois jeux de vérité-terrain : un corpus de 148 binaires compilés par nos soins avec leurs 377 fichiers source d'origine, le Setup Toolkit de VB6 (dont Microsoft publie les sources), et un binaire témoin dont nous possédons le code.
Un défaut qui n'est pas confronté à sa source n'est pas un défaut : c'est une hypothèse.
3 849 épreuves automatisées tournent à chaque modification.
Et par-dessus, le seul critère qui décide vraiment si un export sert à quelque chose : le projet reconstruit se recompile-t-il ? Les 149 projets du banc sont réexportés puis passés à VB6.EXE /make. Au 5 septembre 2026 : 119 se recompilent tels quels, soit 80 %.
SETUP1.EXE n'en fait pas partie — il s'arrête sur un Argument not optional à la ligne 119 de frmSetup1.frm. C'est notre témoin le plus public ; le taire n'aurait tenu qu'un après-midi.
Legacy contre V7, sur les mêmes binaires
Les chiffres ci-dessous sont relevés, pas estimés : les deux produits ont tourné sur les mêmes fichiers, et leurs sorties ont été confrontées à la source VB6 d'origine. La colonne « legacy » est celle de VBReFormer 6.4 — la dernière version qui traite les deux témoins de bout en bout.
SETUP1.EXE — 286 Ko, natif, 16 composants, 238 procédures dans la source :
| VBReFormer legacy | VBReFormer V7 | |
|---|---|---|
| Procédures écrites dans l'export | 136 | 227 |
| Corps vide alors que la source ne l'est pas | 1 | 0 |
| Lignes de code, tous fichiers | 7 464 | 8 119 |
| Ouvrir le binaire (format + localisation) | — | 0,05 s |
| Décompiler les 227 corps | — | 0,64 s (2,8 ms par corps) |
| De l'octet au projet complet | — | 0,95 s |
Un binaire de 9,35 Mo — 165 composants, 1 718 procédures dans la source :
| VBReFormer legacy | VBReFormer V7 | |
|---|---|---|
| Ouverture → projet écrit | ≈ 88 s | 12 s |
| Procédures écrites dans l'export | 1 799 | 1 976 |
| Corps vide alors que la source ne l'est pas | 229 | 0 |
| Procédures dont la structuration aboutit | — | 1 976 / 1 976 |
| Marqueurs d'erreur dans le code rendu | 1 129 | 0 |
Signatures rendues As Unknow | 75 014 | 0 |
Et le test qui ne se résume pas à un compteur : 24 procédures tirées au sort et lues ligne à ligne contre leur source d'origine. La V7 est plus fidèle dans 22 cas, le legacy dans 2.
Les deux endroits où le legacy gagne encore
Un comparatif qui ne perd nulle part est un comparatif qu'on n'a pas fait. Les deux nôtres :
- Les blocs isolés. Quand une procédure contient des blocs qu'aucune arête ne relie à l'entrée — cibles de
On Error GoTo,GoSub,Resume— la V7 les voit, les sert au graphe de flot, et ne les écrit pas dans le source. Sur le témoin : 1 541 blocs perdus, sur 232 procédures (12 %). Le legacy, lui, n'abandonne rien : son émission linéaire écrit tout, mal structuré mais présent. C'est notre défaut ouvert le plus large
Et le legacy porte encore six fonctions sans équivalent V7 sur le bureau — recherche de texte dans le projet, recherche des bibliothèques importées, recherche de programmes VB sur le disque, vue des ressources, export de l'image d'un formulaire, écran d'options.
Aucun des chiffres de cette section n'est une estimation. Le protocole, les scripts et les 24 procédures lues une à une sont publiés avec le produit.
Ce que V7 refuse de faire
Une liste courte, et elle est délibérée.
- Pas de
Withreconstruit. Le compilateur le dissout ; le remettre serait inventer une structure que le binaire ne porte pas. La sortie est pleinement qualifiée (Frame1.Caption) - Pas de valeur plausible. Quand une valeur ne peut pas être rendue juste, V7 écrit
?. Un nombre crédible et faux coûte plus cher qu'un marqueur visible - Pas de nom inventé pour ce qui n'en a pas.
proc_405630dit la vérité ; un nom joli ne la dirait pas
Disponibilité
VBReFormer Online est en bêta dès aujourd'hui — vb.decompiler.tools — et son socle gratuit va loin : désassemblage complet, P-code complet, structure, ressources, concepteur, .vbp reconstruit, et la source en aperçu réel.
VBReFormer V7 (bureau) suit dans les prochaines semaines. Une licence desktop+cloud ouvre les deux.