@@ -2,21 +2,81 @@ package main
22
33import (
44 "context"
5+ "encoding/json"
56 "fmt"
7+ "os"
68
79 "github.com/0xInception/wasmspy/pkg/decompile"
810 "github.com/0xInception/wasmspy/pkg/wasm"
911 "github.com/wailsapp/wails/v2/pkg/runtime"
1012)
1113
14+ type FunctionAnnotation struct {
15+ Name string `json:"name,omitempty"`
16+ Comment string `json:"comment,omitempty"`
17+ }
18+
19+ type Annotations struct {
20+ Version int `json:"version"`
21+ Functions map [string ]* FunctionAnnotation `json:"functions,omitempty"`
22+ Comments map [string ]string `json:"comments,omitempty"`
23+ DecompileComments map [string ]string `json:"decompileComments,omitempty"`
24+ Bookmarks []uint32 `json:"bookmarks,omitempty"`
25+ }
26+
27+ func NewAnnotations () * Annotations {
28+ return & Annotations {
29+ Version : 1 ,
30+ Functions : make (map [string ]* FunctionAnnotation ),
31+ Comments : make (map [string ]string ),
32+ DecompileComments : make (map [string ]string ),
33+ Bookmarks : []uint32 {},
34+ }
35+ }
36+
37+ func annotationsPath (wasmPath string ) string {
38+ return wasmPath + ".wasmspy"
39+ }
40+
41+ func loadAnnotationsFromFile (wasmPath string ) * Annotations {
42+ data , err := os .ReadFile (annotationsPath (wasmPath ))
43+ if err != nil {
44+ return NewAnnotations ()
45+ }
46+ var ann Annotations
47+ if err := json .Unmarshal (data , & ann ); err != nil {
48+ return NewAnnotations ()
49+ }
50+ if ann .Functions == nil {
51+ ann .Functions = make (map [string ]* FunctionAnnotation )
52+ }
53+ if ann .Comments == nil {
54+ ann .Comments = make (map [string ]string )
55+ }
56+ if ann .DecompileComments == nil {
57+ ann .DecompileComments = make (map [string ]string )
58+ }
59+ return & ann
60+ }
61+
62+ func saveAnnotationsToFile (wasmPath string , ann * Annotations ) error {
63+ data , err := json .MarshalIndent (ann , "" , " " )
64+ if err != nil {
65+ return err
66+ }
67+ return os .WriteFile (annotationsPath (wasmPath ), data , 0644 )
68+ }
69+
1270type App struct {
13- ctx context.Context
14- modules map [string ]* wasm.ResolvedModule
71+ ctx context.Context
72+ modules map [string ]* wasm.ResolvedModule
73+ annotations map [string ]* Annotations
1574}
1675
1776func NewApp () * App {
1877 return & App {
19- modules : make (map [string ]* wasm.ResolvedModule ),
78+ modules : make (map [string ]* wasm.ResolvedModule ),
79+ annotations : make (map [string ]* Annotations ),
2080 }
2181}
2282
@@ -35,6 +95,11 @@ func (a *App) onDomReady(ctx context.Context) {
3595 })
3696}
3797
98+ func (a * App ) SetWindowSize (width , height int ) {
99+ runtime .WindowSetSize (a .ctx , width , height )
100+ runtime .WindowCenter (a .ctx )
101+ }
102+
38103func (a * App ) OpenFileDialog () (string , error ) {
39104 path , err := runtime .OpenFileDialog (a .ctx , runtime.OpenDialogOptions {
40105 Title : "Open WebAssembly Module" ,
@@ -101,6 +166,7 @@ func (a *App) loadModule(path string, mod *wasm.Module) (*ModuleInfo, error) {
101166 return nil , err
102167 }
103168 a .modules [path ] = resolved
169+ a .annotations [path ] = loadAnnotationsFromFile (path )
104170
105171 info := & ModuleInfo {}
106172
@@ -130,10 +196,17 @@ func (a *App) loadModule(path string, mod *wasm.Module) (*ModuleInfo, error) {
130196 })
131197 }
132198
199+ ann := a .annotations [path ]
133200 for _ , fn := range resolved .Functions {
201+ name := fn .Name
202+ if ann != nil {
203+ if fa := ann .Functions [fmt .Sprintf ("%d" , fn .Index )]; fa != nil && fa .Name != "" {
204+ name = fa .Name
205+ }
206+ }
134207 fi := FunctionInfo {
135208 Index : fn .Index ,
136- Name : fn . Name ,
209+ Name : name ,
137210 Imported : fn .Imported ,
138211 }
139212 if fn .Type != nil {
@@ -174,7 +247,8 @@ func (a *App) DisassembleFunction(path string, index uint32, indent bool) (strin
174247 if fn == nil {
175248 return "" , fmt .Errorf ("function %d not found" , index )
176249 }
177- return formatFunctionDisasm (fn , module , indent ), nil
250+ ann := a .annotations [path ]
251+ return formatFunctionDisasm (fn , module , indent , ann ), nil
178252}
179253
180254func (a * App ) GetFunctionWAT (path string , index uint32 ) (string , error ) {
@@ -438,13 +512,29 @@ func getDataSegOffset(instrs []wasm.Instruction) uint32 {
438512 return 0
439513}
440514
441- func formatFunctionDisasm (fn * wasm.ResolvedFunction , rm * wasm.ResolvedModule , indented bool ) string {
515+ func formatFunctionDisasm (fn * wasm.ResolvedFunction , rm * wasm.ResolvedModule , indented bool , ann * Annotations ) string {
442516 if fn .Imported {
443517 return fmt .Sprintf ("; imported: %s" , fn .Name )
444518 }
445519
520+ name := fn .Name
521+ funcComment := ""
522+ if ann != nil {
523+ if fa := ann .Functions [fmt .Sprintf ("%d" , fn .Index )]; fa != nil {
524+ if fa .Name != "" {
525+ name = fa .Name
526+ }
527+ if fa .Comment != "" {
528+ funcComment = fa .Comment
529+ }
530+ }
531+ }
532+
446533 var result string
447- result = fmt .Sprintf ("; Function %d: %s\n " , fn .Index , fn .Name )
534+ result = fmt .Sprintf ("; Function %d: %s\n " , fn .Index , name )
535+ if funcComment != "" {
536+ result += fmt .Sprintf ("; %s\n " , funcComment )
537+ }
448538
449539 if fn .Type != nil {
450540 result += fmt .Sprintf ("; Params: %d, Results: %d\n " , len (fn .Type .Params ), len (fn .Type .Results ))
@@ -465,6 +555,13 @@ func formatFunctionDisasm(fn *wasm.ResolvedFunction, rm *wasm.ResolvedModule, in
465555
466556 indent := 0
467557 for _ , instr := range fn .Body .Instructions {
558+ comment := ""
559+ if ann != nil {
560+ if c := ann .Comments [fmt .Sprintf ("0x%x" , instr .Offset )]; c != "" {
561+ comment = " ; " + c
562+ }
563+ }
564+
468565 if indented {
469566 if instr .Opcode == wasm .OpEnd || instr .Opcode == wasm .OpElse {
470567 if indent > 0 {
@@ -476,14 +573,14 @@ func formatFunctionDisasm(fn *wasm.ResolvedFunction, rm *wasm.ResolvedModule, in
476573 for j := 0 ; j < indent ; j ++ {
477574 prefix += " "
478575 }
479- result += fmt .Sprintf ("%08x: %s%s\n " , instr .Offset , prefix , formatInstrWithImm (& instr ))
576+ result += fmt .Sprintf ("%08x: %s%s%s \n " , instr .Offset , prefix , formatInstrWithImm (& instr ), comment )
480577
481578 if instr .Opcode == wasm .OpBlock || instr .Opcode == wasm .OpLoop ||
482579 instr .Opcode == wasm .OpIf || instr .Opcode == wasm .OpElse {
483580 indent ++
484581 }
485582 } else {
486- result += fmt .Sprintf ("%08x: %s\n " , instr .Offset , formatInstrWithImm (& instr ))
583+ result += fmt .Sprintf ("%08x: %s%s \n " , instr .Offset , formatInstrWithImm (& instr ), comment )
487584 }
488585 }
489586 }
@@ -574,3 +671,168 @@ func formatInstr(instr *wasm.Instruction) string {
574671 }
575672 return result
576673}
674+
675+ func (a * App ) GetAnnotations (path string ) * Annotations {
676+ if ann := a .annotations [path ]; ann != nil {
677+ return ann
678+ }
679+ return NewAnnotations ()
680+ }
681+
682+ func (a * App ) SetFunctionName (path string , index uint32 , name string ) error {
683+ ann := a .annotations [path ]
684+ if ann == nil {
685+ ann = NewAnnotations ()
686+ a .annotations [path ] = ann
687+ }
688+ key := fmt .Sprintf ("%d" , index )
689+ if ann .Functions [key ] == nil {
690+ ann .Functions [key ] = & FunctionAnnotation {}
691+ }
692+ ann .Functions [key ].Name = name
693+ return nil
694+ }
695+
696+ func (a * App ) SetFunctionComment (path string , index uint32 , comment string ) error {
697+ ann := a .annotations [path ]
698+ if ann == nil {
699+ ann = NewAnnotations ()
700+ a .annotations [path ] = ann
701+ }
702+ key := fmt .Sprintf ("%d" , index )
703+ if ann .Functions [key ] == nil {
704+ ann .Functions [key ] = & FunctionAnnotation {}
705+ }
706+ ann .Functions [key ].Comment = comment
707+ return nil
708+ }
709+
710+ func (a * App ) SetOffsetComment (path string , offset uint64 , comment string , isDecompile bool ) error {
711+ ann := a .annotations [path ]
712+ if ann == nil {
713+ ann = NewAnnotations ()
714+ a .annotations [path ] = ann
715+ }
716+ key := fmt .Sprintf ("0x%x" , offset )
717+ target := ann .Comments
718+ if isDecompile {
719+ target = ann .DecompileComments
720+ }
721+ if comment == "" {
722+ delete (target , key )
723+ } else {
724+ target [key ] = comment
725+ }
726+ return nil
727+ }
728+
729+ func (a * App ) SetBookmarks (path string , bookmarks []uint32 ) error {
730+ ann := a .annotations [path ]
731+ if ann == nil {
732+ ann = NewAnnotations ()
733+ a .annotations [path ] = ann
734+ }
735+ ann .Bookmarks = bookmarks
736+ return nil
737+ }
738+
739+ func (a * App ) SaveAnnotations (path string ) error {
740+ ann := a .annotations [path ]
741+ if ann == nil {
742+ return nil
743+ }
744+ return saveAnnotationsToFile (path , ann )
745+ }
746+
747+ func (a * App ) ClearAnnotations (path string ) * Annotations {
748+ a .annotations [path ] = NewAnnotations ()
749+ return a .annotations [path ]
750+ }
751+
752+ func (a * App ) ExportAnnotations (path string ) (string , error ) {
753+ ann := a .annotations [path ]
754+ if ann == nil {
755+ ann = NewAnnotations ()
756+ }
757+ data , err := json .MarshalIndent (ann , "" , " " )
758+ if err != nil {
759+ return "" , err
760+ }
761+ return string (data ), nil
762+ }
763+
764+ func (a * App ) ImportAnnotations (path string , data string ) error {
765+ var ann Annotations
766+ if err := json .Unmarshal ([]byte (data ), & ann ); err != nil {
767+ return err
768+ }
769+ if ann .Functions == nil {
770+ ann .Functions = make (map [string ]* FunctionAnnotation )
771+ }
772+ if ann .Comments == nil {
773+ ann .Comments = make (map [string ]string )
774+ }
775+ if ann .DecompileComments == nil {
776+ ann .DecompileComments = make (map [string ]string )
777+ }
778+ a .annotations [path ] = & ann
779+ return nil
780+ }
781+
782+ func (a * App ) ExportAnnotationsToFile (wasmPath string ) (string , error ) {
783+ savePath , err := runtime .SaveFileDialog (a .ctx , runtime.SaveDialogOptions {
784+ Title : "Export Annotations" ,
785+ DefaultFilename : "annotations.wasmspy" ,
786+ Filters : []runtime.FileFilter {
787+ {DisplayName : "WasmSpy Annotations" , Pattern : "*.wasmspy" },
788+ {DisplayName : "JSON Files" , Pattern : "*.json" },
789+ },
790+ })
791+ if err != nil || savePath == "" {
792+ return "" , err
793+ }
794+ ann := a .annotations [wasmPath ]
795+ if ann == nil {
796+ ann = NewAnnotations ()
797+ }
798+ data , err := json .MarshalIndent (ann , "" , " " )
799+ if err != nil {
800+ return "" , err
801+ }
802+ if err := os .WriteFile (savePath , data , 0644 ); err != nil {
803+ return "" , err
804+ }
805+ return savePath , nil
806+ }
807+
808+ func (a * App ) ImportAnnotationsFromFile (wasmPath string ) (string , error ) {
809+ openPath , err := runtime .OpenFileDialog (a .ctx , runtime.OpenDialogOptions {
810+ Title : "Import Annotations" ,
811+ Filters : []runtime.FileFilter {
812+ {DisplayName : "WasmSpy Annotations" , Pattern : "*.wasmspy" },
813+ {DisplayName : "JSON Files" , Pattern : "*.json" },
814+ },
815+ })
816+ if err != nil || openPath == "" {
817+ return "" , err
818+ }
819+ data , err := os .ReadFile (openPath )
820+ if err != nil {
821+ return "" , err
822+ }
823+ var ann Annotations
824+ if err := json .Unmarshal (data , & ann ); err != nil {
825+ return "" , err
826+ }
827+ if ann .Functions == nil {
828+ ann .Functions = make (map [string ]* FunctionAnnotation )
829+ }
830+ if ann .Comments == nil {
831+ ann .Comments = make (map [string ]string )
832+ }
833+ if ann .DecompileComments == nil {
834+ ann .DecompileComments = make (map [string ]string )
835+ }
836+ a .annotations [wasmPath ] = & ann
837+ return openPath , nil
838+ }
0 commit comments