170 lines
3.1 KiB
Go
170 lines
3.1 KiB
Go
package patcher
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/tdewolff/parse/v2"
|
|
"github.com/tdewolff/parse/v2/js"
|
|
"vaclive.party/software/lychee-slicer/asar"
|
|
"vaclive.party/software/lychee-slicer/platform"
|
|
)
|
|
|
|
type walker struct {
|
|
funName string
|
|
fun *js.FuncDecl
|
|
}
|
|
|
|
func (w *walker) Enter(n js.INode) js.IVisitor {
|
|
switch n := n.(type) {
|
|
case *js.BindingElement:
|
|
if binding, ok := n.Binding.(*js.Var); ok && string(binding.Name()) == w.funName {
|
|
if fun, ok := n.Default.(*js.FuncDecl); ok {
|
|
w.fun = fun
|
|
}
|
|
}
|
|
case *js.FuncDecl:
|
|
if n.Name != nil && string(n.Name.Data) == w.funName {
|
|
w.fun = n
|
|
}
|
|
}
|
|
|
|
if w.fun != nil {
|
|
return nil
|
|
}
|
|
return w
|
|
}
|
|
|
|
func (w *walker) Exit(n js.INode) {}
|
|
|
|
func findFunction(ast *js.AST, name string) *js.FuncDecl {
|
|
w := &walker{funName: name}
|
|
js.Walk(w, ast)
|
|
return w.fun
|
|
}
|
|
|
|
func patchFile(program []byte) ([]byte, error) {
|
|
ast, err := js.Parse(parse.NewInputBytes([]byte(program)), js.Options{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
fun := findFunction(ast, "verify")
|
|
if fun == nil {
|
|
return nil, errors.New("verify function not found")
|
|
}
|
|
|
|
// who needs verification anyway?
|
|
fun.Body.List = []js.IStmt{
|
|
&js.ReturnStmt{
|
|
Value: &js.LiteralExpr{
|
|
TokenType: js.TrueToken,
|
|
Data: []byte("true"),
|
|
},
|
|
},
|
|
}
|
|
|
|
stream := bytes.NewBuffer(nil)
|
|
ast.JS(stream)
|
|
|
|
return stream.Bytes(), nil
|
|
}
|
|
|
|
func Patch(program string) error {
|
|
asarFile := filepath.Join(filepath.Dir(program), "resources", "app.asar")
|
|
log.Printf("Opening %s", asarFile)
|
|
|
|
old, err := os.OpenFile(asarFile, os.O_RDONLY, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer old.Close()
|
|
|
|
stat, err := old.Stat()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fs, err := asar.OpenFilesystem(old, stat.Size())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if v := fs.GetMetaFlag("patched"); v != "" {
|
|
log.Printf("Already patched")
|
|
return nil
|
|
}
|
|
|
|
patch := func(name string) error {
|
|
log.Printf("Patching %s", name)
|
|
b, err := fs.ReadFile(name, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
b, err = patchFile(b)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return fs.InsertFile(name, b, false)
|
|
}
|
|
|
|
if err := patch("node_modules/@mango3d/sign/dist/cjs/index.js"); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := patch("node_modules/@mango3d/sign/dist/esm/index.js"); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := fs.SetMetaFlag("patched", "true"); err != nil {
|
|
return err
|
|
}
|
|
|
|
saved, err := os.CreateTemp("", "patched")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer saved.Close()
|
|
defer os.Remove(saved.Name())
|
|
|
|
if err := fs.Save(saved); err != nil {
|
|
return err
|
|
}
|
|
|
|
fs = nil
|
|
old.Close()
|
|
|
|
_, err = saved.Seek(0, io.SeekStart)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Printf("Replacing original asar with patched")
|
|
new, err := os.Create(asarFile)
|
|
if err != nil {
|
|
if err, ok := err.(*os.PathError); ok && platform.IsWindowsRetryable(err.Err) {
|
|
log.Printf("Windows rename failed, retrying")
|
|
saved.Close()
|
|
|
|
return platform.WindowsRename(saved.Name(), asarFile)
|
|
}
|
|
return err
|
|
}
|
|
defer new.Close()
|
|
|
|
_, err = io.Copy(new, saved)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
new.Close()
|
|
saved.Close()
|
|
|
|
return err
|
|
}
|