From fe0d580cf4cebe7982b42e3f5d728c61d84faab4 Mon Sep 17 00:00:00 2001 From: cdle Date: Sun, 2 Jul 2023 18:45:12 +0800 Subject: [PATCH] x --- core/node_ crypto.go | 355 +++++++++++++++++++++++++++++++++++++------ core/node_buffer.go | 230 ++++++++++++++++------------ core/plugin_core.go | 22 ++- core/plugin_utils.go | 8 +- 4 files changed, 459 insertions(+), 156 deletions(-) diff --git a/core/node_ crypto.go b/core/node_ crypto.go index c6d4ccf..5709013 100644 --- a/core/node_ crypto.go +++ b/core/node_ crypto.go @@ -1,39 +1,133 @@ package core import ( + "bytes" + "crypto/aes" + "crypto/cipher" "crypto/hmac" "crypto/md5" "crypto/sha1" "crypto/sha256" "crypto/sha512" "encoding/base64" + "encoding/hex" "fmt" "hash" + + "github.com/dop251/goja" ) -type Crypto struct{} - -type Hmac struct { - Algorithm string - hash hash.Hash +func toBytes(v interface{}) []byte { + switch v := v.(type) { + case []byte: + return v + case string: + return []byte(v) + case *Buffer: + return v.value + } + return nil } -func (h *Hmac) Digest(class string) string { - value := h.hash.Sum(nil) - switch class { - case "hex": - return fmt.Sprintf("%x", value) - case "base64": - return base64.StdEncoding.EncodeToString(value) +func toString(v interface{}) string { + switch v := v.(type) { + case []byte: + return string(v) + case string: + return v + case *Buffer: + return string(v.value) } return "" } -func (h *Hmac) Update(data string) { - h.hash.Write([]byte(data)) +type Crypto struct { + vm *goja.Runtime } -func (c *Crypto) CreateHmac(algorithm, key string) *Hmac { +type Hmac struct { + // decode bool + vm *goja.Runtime + Algorithm string + middle interface{} +} + +func (h *Hmac) Digest(EF string) interface{} { + var result interface{} + var SF = "" + switch h.Algorithm { + case "md5", "sha1", "sha256", "sha512": + result = h.middle.(hash.Hash).Sum(nil) + case "aes-256-cbc": + } + if EF == "" { + return &Buffer{ + value: result.([]byte), + } + } + return Convert(h.vm, result, SF, EF) +} + +func (h *Hmac) Update(data interface{}, SF, EF string) interface{} { + switch h.Algorithm { + case "md5", "sha1", "sha256", "sha512": + h.middle.(hash.Hash).Write(toBytes(data)) + case "aes-256-cbc": + if SF == "hex" { + data = Convert(h.vm, data, "hex", "") + } + return Convert(h.vm, h.middle.(*AesCipher).Update(toBytes(data)), "", EF) + } + return nil +} + +func (h *Hmac) Final(EF string) interface{} { + switch h.Algorithm { + case "md5", "sha1", "sha256", "sha512": + case "aes-256-cbc": + return Convert(h.vm, h.middle.(*AesCipher).Final(), "", EF) + } + return nil +} + +func (c *Crypto) CreateCipheriv(algorithm string, key, iv interface{}) *Hmac { + var middle interface{} + var err error + switch algorithm { + case "aes-256-cbc": + + middle, err = NewAesCipher(c.vm, toBytes(key), toBytes(iv), false) + if err != nil { + panic(Error(c.vm, err)) + } + } + return &Hmac{ + vm: c.vm, + // decode: true, + middle: middle, + Algorithm: algorithm, + } +} + +func (c *Crypto) CreateDecipheriv(algorithm string, key, iv interface{}) *Hmac { + var middle interface{} + var err error + switch algorithm { + case "aes-256-cbc": + middle, err = NewAesCipher(c.vm, toBytes(key), toBytes(iv), true) + if err != nil { + panic(Error(c.vm, err)) + } + } + return &Hmac{ + // decode: true, + vm: c.vm, + middle: middle, + Algorithm: algorithm, + } +} + +func (c *Crypto) CreateHash(algorithm string) *Hmac { var hashFunc func() hash.Hash switch algorithm { case "md5": @@ -48,28 +142,95 @@ func (c *Crypto) CreateHmac(algorithm, key string) *Hmac { return nil } return &Hmac{ - hash: hmac.New(hashFunc, []byte(key)), + vm: c.vm, + middle: hashFunc(), Algorithm: algorithm, } } -// func (c *Crypto) CreateCipheriv(algorithm, key, data string) *Hmac { -// switch algorithm { -// case "aes-256-cbc": -// block, err := aes.NewCipher([]byte(key)) -// if err != nil { -// return "", err -// } -// ciphertext := make([]byte, aes.BlockSize+len(data)) -// iv := ciphertext[:aes.BlockSize] -// if _, err := io.ReadFull(rand.Reader, iv); err != nil { -// return "", err -// } -// stream := cipher.NewCFBEncrypter(block, iv) -// stream.XORKeyStream(ciphertext[aes.BlockSize:], []byte(data)) -// return base64.StdEncoding.EncodeToString(ciphertext), nil -// } -// } +func (c *Crypto) CreateHmac(algorithm string, key interface{}) *Hmac { + var hashFunc func() hash.Hash + switch algorithm { + case "md5": + hashFunc = md5.New + case "sha1": + hashFunc = sha1.New + case "sha256": + hashFunc = sha256.New + case "sha512": + hashFunc = sha512.New + default: + return nil + } + return &Hmac{ + vm: c.vm, + middle: hmac.New(hashFunc, toBytes(key)), + Algorithm: algorithm, + } +} + +type AesCipher struct { + block cipher.Block + iv []byte + mode cipher.BlockMode + decode bool + buffer bytes.Buffer + vm *goja.Runtime +} + +func NewAesCipher(vm *goja.Runtime, key, iv []byte, decode bool) (*AesCipher, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + mode := cipher.NewCBCDecrypter(block, iv) + if !decode { + mode = cipher.NewCBCEncrypter(block, iv) + } + return &AesCipher{ + vm: vm, + block: block, + iv: iv, + mode: mode, + decode: decode, + }, nil +} + +func (ac *AesCipher) Update(text []byte) []byte { + //fmt.Println("Update", string(text)) + if ac.decode { + // Decrypt + var err error + //fmt.Println("Decrypt1", string(text)) + ac.mode.CryptBlocks(text, text) + //fmt.Println("Decrypt2", string(text)) + text, err = pkcs7Unpadding(text) + if err != nil { + panic(Error(ac.vm, err)) + } + //fmt.Println("Decrypt3", string(text), err) + } else { + // Encrypt + text = pkcs7Padding(text, ac.block.BlockSize()) + ac.mode.CryptBlocks(text, text) + } + ac.buffer.Write(text) + return nil + // return text +} + +func (ac *AesCipher) Final() []byte { + return ac.buffer.Bytes() +} + +func pkcs7Unpadding(data []byte) ([]byte, error) { + length := len(data) + unpadding := int(data[length-1]) + if unpadding > length { + return nil, fmt.Errorf("pkcs7: invalid padding") + } + return data[:(length - unpadding)], nil +} // type Cipher struct{} @@ -117,15 +278,119 @@ func (c *Crypto) CreateHmac(algorithm, key string) *Hmac { // return base64.StdEncoding.EncodeToString(ciphertext), nil // } -// func (c *Cipher) Rc4Decrypt(key, ciphertext string) (string, error) { -// cipher, err := rc4.NewCipher([]byte(key)) -// if err != nil { -// return "", err -// } -// data, err := base64.StdEncoding.DecodeString(ciphertext) -// if err != nil { -// return "", err -// } -// cipher.XORKeyStream(data, data) -// return string(data), nil -// } +// func (c *Cipher) Rc4Decrypt(key, ciphertext string) (string, error) { +// cipher, err := rc4.NewCipher([]byte(key)) +// if err != nil { +// return "", err +// } +// data, err := base64.StdEncoding.DecodeString(ciphertext) +// if err != nil { +// return "", err +// } +// cipher.XORKeyStream(data, data) +// return string(data), nil +// } + +func Convert(vm *goja.Runtime, data interface{}, fromFormat string, toFormat string) interface{} { + //fmt.Println(data, fromFormat, toFormat) + var dataBytes []byte + + switch input := data.(type) { + case string: + dataBytes = []byte(input) + case []byte: + dataBytes = input + case *Buffer: + dataBytes = input.value + default: + panic(Error(vm, fmt.Errorf("invalid data type: %T", data))) + } + + switch fromFormat { + case "hex": + // 将数据从 HEX 编码格式转换为字节数组 + decodedData, err := hex.DecodeString(string(dataBytes)) + if err != nil { + panic(Error(vm, err)) + } + dataBytes = decodedData + case "base64": + // 将数据从 Base64 编码格式转换为字节数组 + decodedData, err := base64.StdEncoding.DecodeString(string(dataBytes)) + if err != nil { + panic(Error(vm, err)) + } + dataBytes = decodedData + case "bytes", "binary": + // 不需要进行转换 + case "utf8", "utf-8", "": + + default: + panic(Error(vm, fmt.Errorf("unsupported input format: %s", fromFormat))) + } + + //fmt.Println(string(dataBytes), fromFormat, toFormat) + + switch toFormat { + case "hex": + // 将数据转换为 HEX 编码格式 + //fmt.Println("hex", hex.EncodeToString(dataBytes), fromFormat, toFormat) + return hex.EncodeToString(dataBytes) + case "base64": + // 将数据转换为 Base64 编码格式 + return base64.StdEncoding.EncodeToString(dataBytes) + case "bytes", "binary": + // 不需要进行转换 + return dataBytes + case "utf8", "utf-8", "": + // 将数据转换为 UTF-8 编码格式 + return string(dataBytes) + default: + panic(Error(vm, fmt.Errorf("unsupported output format: %s", toFormat))) + } +} + +// panic: interface conversion: interface {} is *cipher.cfb, not *core.AesCipher [recovered] +// panic: interface conversion: interface {} is *cipher.cfb, not *core.AesCipher + +// goroutine 15 [running]: +// github.com/dop251/goja.(*Runtime).RunProgram.func1() +// C:/Users/Administrator/go/pkg/mod/github.com/dop251/goja@v0.0.0-20230605162241-28ee0ee714f3/runtime.go:1491 +0x3b8 +// panic({0x1a5f6e0, 0xc0017a4780}) +// C:/Program Files/Go/src/runtime/panic.go:884 +0x213 +// github.com/dop251/goja.(*vm).handleThrow(0xc001312240, {0x1a5f6e0, 0xc0017a4780}) +// C:/Users/Administrator/go/pkg/mod/github.com/dop251/goja@v0.0.0-20230605162241-28ee0ee714f3/vm.go:788 +0x497 +// github.com/dop251/goja.(*vm).runTryInner.func1() +// C:/Users/Administrator/go/pkg/mod/github.com/dop251/goja@v0.0.0-20230605162241-28ee0ee714f3/vm.go:830 +0x45 +// panic({0x1a5f6e0, 0xc0017a4780}) +// C:/Program Files/Go/src/runtime/panic.go:884 +0x213 +// github.com/cdle/sillyplus/core.(*Hmac).Final(0x2?, {0xc0008266d4?, 0x1?}) +// C:/Users/Administrator/Desktop/sillyplus/core/node_ crypto.go:90 +0x145 +// reflect.Value.call({0x1a8dfc0?, 0xc0017a43c0?, 0x1c5efb2f198?}, {0x1b85371, 0x4}, {0xc0009fc060, 0x1, 0x0?}) +// C:/Program Files/Go/src/reflect/value.go:586 +0xb0b +// reflect.Value.Call({0x1a8dfc0?, 0xc0017a43c0?, 0xc000f687c0?}, {0xc0009fc060?, 0xc001076630?, 0x0?}) +// C:/Program Files/Go/src/reflect/value.go:370 +0xbc +// github.com/dop251/goja.(*Runtime).wrapReflectFunc.func1({{0x2139e58, 0xc0017a43f0}, {0xc000110aa0, 0x1, 0x46}}) +// C:/Users/Administrator/go/pkg/mod/github.com/dop251/goja@v0.0.0-20230605162241-28ee0ee714f3/runtime.go:2056 +0x3dc +// github.com/dop251/goja.(*nativeFuncObject).vmCall(0xc001784780, 0xc001312240, 0x1) +// C:/Users/Administrator/go/pkg/mod/github.com/dop251/goja@v0.0.0-20230605162241-28ee0ee714f3/func.go:559 +0x1af +// github.com/dop251/goja.call.exec(0x5?, 0xc001312240) +// C:/Users/Administrator/go/pkg/mod/github.com/dop251/goja@v0.0.0-20230605162241-28ee0ee714f3/vm.go:3366 +0x6a +// github.com/dop251/goja.(*vm).run(0xc001312240) +// C:/Users/Administrator/go/pkg/mod/github.com/dop251/goja@v0.0.0-20230605162241-28ee0ee714f3/vm.go:582 +0x62 +// github.com/dop251/goja.(*vm).runTryInner(0x1b16220?) +// C:/Users/Administrator/go/pkg/mod/github.com/dop251/goja@v0.0.0-20230605162241-28ee0ee714f3/vm.go:834 +0x70 +// github.com/dop251/goja.(*vm).runTry(0xc001312240) +// C:/Users/Administrator/go/pkg/mod/github.com/dop251/goja@v0.0.0-20230605162241-28ee0ee714f3/vm.go:820 +0x1c9 +// github.com/dop251/goja.(*Runtime).RunProgram(0xc000b89c00, 0xc000ef1f20) +// C:/Users/Administrator/go/pkg/mod/github.com/dop251/goja@v0.0.0-20230605162241-28ee0ee714f3/runtime.go:1513 +0x48d +// github.com/cdle/sillyplus/core.initPlugin.func3.1(0xc000b89c00) +// C:/Users/Administrator/Desktop/sillyplus/core/plugin_core.go:585 +0x61d +// github.com/dop251/goja_nodejs/eventloop.(*EventLoop).Run(0xc001583b00, 0xc00126df20) +// C:/Users/Administrator/go/pkg/mod/github.com/dop251/goja_nodejs@v0.0.0-20230602164024-804a84515562/eventloop/eventloop.go:203 +0x37 +// github.com/cdle/sillyplus/core.initPlugin.func3({0x2141bb8, 0xc001312120}, 0x0) +// C:/Users/Administrator/Desktop/sillyplus/core/plugin_core.go:511 +0x126 +// github.com/cdle/sillyplus/core.AddCommand.func1(0xc000ac1d40) +// C:/Users/Administrator/Desktop/sillyplus/core/function.go:372 +0xcb +// created by github.com/cdle/sillyplus/core.AddCommand +// C:/Users/Administrator/Desktop/sillyplus/core/function.go:369 +0xfe diff --git a/core/node_buffer.go b/core/node_buffer.go index 17457bd..e8aeeb2 100644 --- a/core/node_buffer.go +++ b/core/node_buffer.go @@ -6,104 +6,134 @@ import ( "github.com/dop251/goja" ) -func Buffer(vm *goja.Runtime, call goja.ConstructorCall) *goja.Object { - var buffer []byte - switch arg := call.Arguments[0].Export().(type) { - case string: - buffer = []byte(arg) - case []byte: - buffer = arg - case int: - buffer = make([]byte, arg) - case int64: - buffer = make([]byte, arg) - default: - panic(vm.NewTypeError("invalid argument type")) - } - obj := call.This.ToObject(vm) - obj.Set("length", func() int { - return len(buffer) - }) - - obj.Set("write", func(value interface{}, offset int, length int) { - var buf []byte - switch value := value.(type) { - case string: - buf = []byte(value) - case []byte: - buf = value - default: - panic(vm.NewTypeError("invalid argument type")) - } - - if offset+length > len(buffer) { - panic(vm.NewGoError(errors.New("out of range"))) - } - - copy(buffer[offset:], buf[:length]) - }) - - obj.Set("toString", func() string { - return string(buffer) - }) - - obj.Set("slice", func(start int, end int) []byte { - if start < 0 { - start += len(buffer) - } - if end < 0 { - end += len(buffer) - } - if end > len(buffer) { - end = len(buffer) - } - if start >= end || start >= len(buffer) { - return nil - } - return buffer[start:end] - }) - - obj.Set("copy", func(target []byte, sourceStart int, targetStart int, sourceEnd int) int { - if sourceStart < 0 { - sourceStart += len(buffer) - } - if sourceEnd < 0 { - sourceEnd += len(buffer) - } - if targetStart < 0 { - targetStart += len(target) - } - if sourceEnd > len(buffer) { - sourceEnd = len(buffer) - } - if sourceStart >= sourceEnd || sourceStart >= len(buffer) { - return 0 - } - return copy(target[targetStart:], buffer[sourceStart:sourceEnd]) - }) - - obj.Set("join", func(sep []byte, buffers ...[]byte) []byte { - var totalLen int - for _, buf := range buffers { - totalLen += len(buf) - } - if len(sep) > 0 && len(buffers) > 1 { - totalLen += len(sep) * (len(buffers) - 1) - } - - result := make([]byte, totalLen) - offset := 0 - for i, buf := range buffers { - copy(result[offset:], buf) - offset += len(buf) - if i < len(buffers)-1 && len(sep) > 0 { - copy(result[offset:], sep) - offset += len(sep) - } - } - - return result - }) - - return call.This +type BufferObj struct { + vm *goja.Runtime +} + +func (b *BufferObj) From(v interface{}, EF string) *Buffer { + var buffer []byte + switch v := v.(type) { + case string: + if EF != "" { + buffer = Convert(b.vm, v, EF, "bytes").([]byte) + } else { + buffer = []byte(v) + } + case []byte: + if EF != "" { + buffer = Convert(b.vm, v, EF, "bytes").([]byte) + } else { + buffer = v + } + case int: + buffer = make([]byte, v) + case int64: + buffer = make([]byte, v) + default: + panic(b.vm.NewTypeError("invalid argument type")) + } + return &Buffer{ + vm: b.vm, + value: buffer, + } +} + +func (b *BufferObj) Alloc(v int) *Buffer { + return &Buffer{ + vm: b.vm, + value: make([]byte, v), + } +} + +type Buffer struct { + value []byte + vm *goja.Runtime +} + +func (b *Buffer) Length() int { + return len(b.value) +} + +func (b *Buffer) Write(value interface{}, offset int, length int) int { + var buf []byte + switch value := value.(type) { + case string: + buf = []byte(value) + case []byte: + buf = value + default: + panic(b.vm.NewTypeError("invalid argument type")) + } + + if offset+length > len(b.value) { + panic(b.vm.NewGoError(errors.New("out of range"))) + } + copy(b.value[offset:], buf[:length]) + return len(b.value) +} + +func (b *Buffer) ToString(EF string) interface{} { + if EF != "" { + return Convert(b.vm, b.value, "", EF) + } + return string(b.value) +} + +func (b *Buffer) Slice(start int, end int) *Buffer { + if start < 0 { + start += len(b.value) + } + if end <= 0 { + end += len(b.value) + } + if end > len(b.value) { + end = len(b.value) + } + if start >= end || start >= len(b.value) { + return nil + } + return &Buffer{ + value: b.value[start:end], + } +} + +func (b *Buffer) Copy(target []byte, sourceStart int, targetStart int, sourceEnd int) int { + if sourceStart < 0 { + sourceStart += len(b.value) + } + if sourceEnd < 0 { + sourceEnd += len(b.value) + } + if targetStart < 0 { + targetStart += len(target) + } + if sourceEnd > len(b.value) { + sourceEnd = len(b.value) + } + if sourceStart >= sourceEnd || sourceStart >= len(b.value) { + return 0 + } + return copy(target[targetStart:], b.value[sourceStart:sourceEnd]) +} + +func (b *Buffer) Join(sep []byte, buffers ...[]byte) []byte { + var totalLen int + for _, buf := range buffers { + totalLen += len(buf) + } + if len(sep) > 0 && len(buffers) > 1 { + totalLen += len(sep) * (len(buffers) - 1) + } + + result := make([]byte, totalLen) + offset := 0 + for i, buf := range buffers { + copy(result[offset:], buf) + offset += len(buf) + if i < len(buffers)-1 && len(sep) > 0 { + copy(result[offset:], sep) + offset += len(sep) + } + } + return result } diff --git a/core/plugin_core.go b/core/plugin_core.go index 6306827..677dda6 100644 --- a/core/plugin_core.go +++ b/core/plugin_core.go @@ -81,7 +81,13 @@ func CancelPluginlistening(uuid string) { } } +var debug = sillyGirl.GetBool("debug", false) + func initPlugins() { + storage.Watch(sillyGirl, "debug", func(old, new, key string) *storage.Final { + debug = new == "true" + return nil + }) plugins.Foreach(func(key, data []byte) error { f, cbs, err := initPlugin(string(data), string(key)) if err != nil { @@ -497,13 +503,15 @@ func initPlugin(data string, uuid string) (*common.Function, []func(), error) { f := &common.Function{ Handle: func(s common.Sender, set func(vm *goja.Runtime)) interface{} { - defer func() { - err := recover() - if err != nil { - pluginConsole(uuid).Error("脚本错误:", err) - // s.Reply(fmt.Sprint(err)) - } - }() + if !debug { + defer func() { + err := recover() + if err != nil { + pluginConsole(uuid).Error("脚本错误:", err) + // s.Reply(fmt.Sprint(err)) + } + }() + } if err2 != nil { panic(err2) } diff --git a/core/plugin_utils.go b/core/plugin_utils.go index 6c6b5e2..5e5b211 100644 --- a/core/plugin_utils.go +++ b/core/plugin_utils.go @@ -347,9 +347,7 @@ func SetPluginMethod(vm *goja.Runtime, uuid string, on_start bool, running func( // 字符串转Base64编码 vm.Set("stringToBase64", stringToBase64) vm.Set("base64ToString", base64ToString) - vm.Set("Buffer", func(call goja.ConstructorCall) *goja.Object { - return Buffer(vm, call) - }) + vm.Set("Buffer", &BufferObj{vm: vm}) vm.Set("fetch", func(wts ...interface{}) interface{} { promise, resolve, reject := vm.NewPromise() go func() { @@ -443,7 +441,9 @@ func SetPluginMethod(vm *goja.Runtime, uuid string, on_start bool, running func( } return nil }) - vm.Set("crypto", &Crypto{}) + vm.Set("crypto", &Crypto{ + vm: vm, + }) } func EncryptPlugin(script string) string {