Files
go-obfuscation/pkg/vm/opcodes.go
T
2026-05-31 01:09:34 +01:00

147 lines
2.9 KiB
Go

package vm
import (
"encoding/binary"
"fmt"
)
type Opcode byte
const (
OpNop Opcode = 0x00
OpPush Opcode = 0x01
OpPop Opcode = 0x02
OpDup Opcode = 0x03
OpAdd Opcode = 0x04
OpSub Opcode = 0x05
OpMul Opcode = 0x06
OpDiv Opcode = 0x07
OpMod Opcode = 0x08
OpXor Opcode = 0x09
OpAnd Opcode = 0x0A
OpOr Opcode = 0x0B
OpNot Opcode = 0x0C
OpShl Opcode = 0x0D
OpShr Opcode = 0x0E
OpCmpEq Opcode = 0x0F
OpCmpLt Opcode = 0x10
OpCmpGt Opcode = 0x11
OpJmp Opcode = 0x12
OpJz Opcode = 0x13
OpJnz Opcode = 0x14
OpLoad Opcode = 0x15
OpStore Opcode = 0x16
OpCall Opcode = 0x17
OpRet Opcode = 0x18
OpHalt Opcode = 0x19
OpSwap Opcode = 0x1A
OpRot Opcode = 0x1B
opJunkA Opcode = 0x1C
opJunkB Opcode = 0x1D
opJunkC Opcode = 0x1E
opJunkD Opcode = 0x1F
)
const dispatchKey byte = 0xA5
var opcodeNames = map[Opcode]string{
OpNop: "NOP", OpPush: "PUSH", OpPop: "POP", OpDup: "DUP",
OpAdd: "ADD", OpSub: "SUB", OpMul: "MUL", OpDiv: "DIV",
OpMod: "MOD", OpXor: "XOR", OpAnd: "AND", OpOr: "OR",
OpNot: "NOT", OpShl: "SHL", OpShr: "SHR", OpCmpEq: "CMP_EQ",
OpCmpLt: "CMP_LT", OpCmpGt: "CMP_GT", OpJmp: "JMP", OpJz: "JZ",
OpJnz: "JNZ", OpLoad: "LOAD", OpStore: "STORE", OpCall: "CALL",
OpRet: "RET", OpHalt: "HALT", OpSwap: "SWAP", OpRot: "ROT",
}
func (o Opcode) String() string {
if name, ok := opcodeNames[o]; ok {
return name
}
return fmt.Sprintf("UNK(0x%02X)", byte(o))
}
func encodeOp(op Opcode) byte {
return byte(op) ^ dispatchKey
}
func decodeOp(b byte) Opcode {
return Opcode(b ^ dispatchKey)
}
type argEncoding int
const (
argNone argEncoding = iota
argI8
argI16
argI32
argI64
)
func opArgEnc(op Opcode) argEncoding {
switch op {
case OpPush:
return argI64
case OpShl, OpShr, OpSwap:
return argI8
case OpJmp, OpJz, OpJnz:
return argI32
case OpLoad, OpStore, OpCall:
return argI16
default:
return argNone
}
}
func InstrSize(op Opcode) int {
switch opArgEnc(op) {
case argI64:
return 9
case argI32:
return 5
case argI16:
return 3
case argI8:
return 2
default:
return 1
}
}
func EncodeInstr(op Opcode, arg int64) []byte {
size := InstrSize(op)
buf := make([]byte, size)
buf[0] = encodeOp(op)
switch opArgEnc(op) {
case argI64:
binary.LittleEndian.PutUint64(buf[1:], uint64(arg))
case argI32:
binary.LittleEndian.PutUint32(buf[1:], uint32(arg))
case argI16:
binary.LittleEndian.PutUint16(buf[1:], uint16(arg))
case argI8:
buf[1] = byte(arg)
}
return buf
}
func DecodeInstr(code []byte, pc int) (Opcode, int64, int) {
op := decodeOp(code[pc])
size := InstrSize(op)
var arg int64
switch opArgEnc(op) {
case argI64:
arg = int64(binary.LittleEndian.Uint64(code[pc+1 : pc+9]))
case argI32:
arg = int64(binary.LittleEndian.Uint32(code[pc+1 : pc+5]))
case argI16:
arg = int64(binary.LittleEndian.Uint16(code[pc+1 : pc+3]))
case argI8:
arg = int64(code[pc+1])
}
return op, arg, size
}