mirror of
https://github.com/lukaszraczylo/go-obfuscation.git
synced 2026-10-14 05:48:22 +00:00
v0.0.2
This commit is contained in:
+6
-150
@@ -1,20 +1,12 @@
|
||||
package antidebug
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Check() error {
|
||||
if runtime.GOOS == "linux" {
|
||||
if err := checkPtrace(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := disableCoreDump(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkPtrace(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := disableCoreDump(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := checkTiming(); err != nil {
|
||||
@@ -35,139 +27,3 @@ func Check() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkPtrace() error {
|
||||
self, err := os.ReadFile("/proc/self/status")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
data := string(self)
|
||||
for i := 0; i < len(data)-11; i++ {
|
||||
if data[i:i+11] == "TracerPid:\t" {
|
||||
j := i + 11
|
||||
for j < len(data) && data[j] >= '0' && data[j] <= '9' {
|
||||
j++
|
||||
}
|
||||
pid := data[i+11 : j]
|
||||
if pid != "0" {
|
||||
return fmt.Errorf("antidebug: debugger detected (tracer pid=%s)", pid)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func disableCoreDump() error {
|
||||
f, err := os.OpenFile("/proc/self/coredump_filter", os.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer f.Close()
|
||||
_, _ = f.WriteString("0")
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkTiming() error {
|
||||
start := time.Now()
|
||||
sum := 0
|
||||
for i := 0; i < 1000000; i++ {
|
||||
sum += i
|
||||
}
|
||||
elapsed := time.Since(start)
|
||||
|
||||
if elapsed > 200*time.Millisecond {
|
||||
return fmt.Errorf("antidebug: timing anomaly detected (%v for 1M iterations)", elapsed)
|
||||
}
|
||||
|
||||
_ = sum
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkEnv() error {
|
||||
suspiciousVars := []string{
|
||||
"GODEBUG",
|
||||
"DELVE_LISTENER",
|
||||
"DEBUGINFOD_URL",
|
||||
"RR_LOG_FILE",
|
||||
"_JAVA_OPTIONS",
|
||||
"LD_PRELOAD",
|
||||
"DYLD_INSERT_LIBRARIES",
|
||||
}
|
||||
|
||||
for _, v := range suspiciousVars {
|
||||
if os.Getenv(v) != "" {
|
||||
return fmt.Errorf("antidebug: suspicious env var %s=%s", v, os.Getenv(v))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkParentProcess() error {
|
||||
if runtime.GOOS != "linux" {
|
||||
return nil
|
||||
}
|
||||
|
||||
ppid := os.Getppid()
|
||||
if ppid <= 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
commPath := fmt.Sprintf("/proc/%d/comm", ppid)
|
||||
comm, err := os.ReadFile(commPath)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
parent := string(comm)
|
||||
debuggers := []string{
|
||||
"gdb", "lldb", "strace", "ltrace", "ida", "ida64",
|
||||
"radare2", "r2", "ghidra", "x64dbg", "ollydbg",
|
||||
"delve", "debugserver",
|
||||
}
|
||||
|
||||
for _, d := range debuggers {
|
||||
if len(parent) >= len(d) && parent[:len(d)] == d {
|
||||
return fmt.Errorf("antidebug: parent process is debugger: %s", parent)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func AntiAttach() {
|
||||
if runtime.GOOS != "linux" {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
if err := checkPtrace(); err != nil {
|
||||
os.Exit(66)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func AntiDump() {
|
||||
if runtime.GOOS != "linux" {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
ticker := time.NewTicker(2 * time.Second)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
_ = disableCoreDump()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func CheckHooks() error {
|
||||
if err := checkFunctionPrologues(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := scanForBreakpoints(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
//go:build darwin
|
||||
|
||||
package antidebug
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
ctlKern = 1
|
||||
kernProc = 14
|
||||
kernProcPID = 1
|
||||
pTraced = 0x00000800
|
||||
ptDenyAttach = 31
|
||||
)
|
||||
|
||||
func checkPtrace() error {
|
||||
pid := os.Getpid()
|
||||
|
||||
var mib [4]int32
|
||||
mib[0] = ctlKern
|
||||
mib[1] = kernProc
|
||||
mib[2] = kernProcPID
|
||||
mib[3] = int32(pid)
|
||||
|
||||
bufSize := uintptr(64)
|
||||
buf := make([]byte, bufSize)
|
||||
|
||||
_, _, errno := syscall.Syscall6(
|
||||
uintptr(0x2000000+202),
|
||||
uintptr(unsafe.Pointer(&mib[0])),
|
||||
4,
|
||||
uintptr(unsafe.Pointer(&buf[0])),
|
||||
uintptr(unsafe.Pointer(&bufSize)),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
|
||||
if errno != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if bufSize < 36 {
|
||||
return nil
|
||||
}
|
||||
|
||||
pFlag := uint32(buf[32]) | uint32(buf[33])<<8 | uint32(buf[34])<<16 | uint32(buf[35])<<24
|
||||
|
||||
if pFlag&pTraced != 0 {
|
||||
return fmt.Errorf("antidebug: debugger detected via sysctl P_TRACED (pid=%d)", pid)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func denyPtraceAttach() {
|
||||
pid := os.Getpid()
|
||||
syscall.RawSyscall(
|
||||
uintptr(syscall.SYS_PTRACE),
|
||||
uintptr(ptDenyAttach),
|
||||
uintptr(pid),
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
func disableCoreDump() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkTiming() error {
|
||||
start := time.Now()
|
||||
sum := 0
|
||||
for i := 0; i < 1000000; i++ {
|
||||
sum += i
|
||||
}
|
||||
elapsed := time.Since(start)
|
||||
|
||||
if elapsed > 200*time.Millisecond {
|
||||
return fmt.Errorf("antidebug: timing anomaly detected (%v for 1M iterations)", elapsed)
|
||||
}
|
||||
|
||||
_ = sum
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkEnv() error {
|
||||
suspiciousVars := []string{
|
||||
"DYLD_INSERT_LIBRARIES",
|
||||
"DYLD_FRAMEWORK_PATH",
|
||||
"DYLD_LIBRARY_PATH",
|
||||
"DYLD_FALLBACK_FRAMEWORK_PATH",
|
||||
"DYLD_FALLBACK_LIBRARY_PATH",
|
||||
"DYLD_ROOT_PATH",
|
||||
"GODEBUG",
|
||||
"DELVE_LISTENER",
|
||||
"DEBUGINFOD_URL",
|
||||
"RR_LOG_FILE",
|
||||
"_JAVA_OPTIONS",
|
||||
}
|
||||
|
||||
for _, v := range suspiciousVars {
|
||||
if os.Getenv(v) != "" {
|
||||
return fmt.Errorf("antidebug: suspicious env var %s=%s", v, os.Getenv(v))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkParentProcess() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func AntiAttach() {
|
||||
go func() {
|
||||
denyPtraceAttach()
|
||||
for {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
if err := checkPtrace(); err != nil {
|
||||
os.Exit(66)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func AntiDump() {
|
||||
go func() {
|
||||
ticker := time.NewTicker(2 * time.Second)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
_ = disableCoreDump()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func CheckHooks() error {
|
||||
if err := checkFunctionPrologues(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := scanForBreakpoints(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -2,10 +2,116 @@
|
||||
|
||||
package antidebug
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func checkFunctionPrologues() error {
|
||||
type target struct {
|
||||
name string
|
||||
addr uintptr
|
||||
}
|
||||
|
||||
targets := []target{
|
||||
{"os.Exit", reflect.ValueOf(os.Exit).Pointer()},
|
||||
{"runtime.Goexit", reflect.ValueOf(runtime.Goexit).Pointer()},
|
||||
}
|
||||
|
||||
for _, t := range targets {
|
||||
if t.addr == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
prologue := readAddr(t.addr, 8)
|
||||
if len(prologue) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
if prologue[0] == 0xE9 {
|
||||
return fmt.Errorf("antidebug: JMP hook detected at %s (0x%x)", t.name, t.addr)
|
||||
}
|
||||
if prologue[0] == 0xEB {
|
||||
return fmt.Errorf("antidebug: short JMP hook detected at %s (0x%x)", t.name, t.addr)
|
||||
}
|
||||
if prologue[0] == 0xFF && prologue[1] == 0x25 {
|
||||
return fmt.Errorf("antidebug: indirect JMP hook detected at %s (0x%x)", t.name, t.addr)
|
||||
}
|
||||
if prologue[0] == 0xCC && prologue[1] == 0xCC {
|
||||
return fmt.Errorf("antidebug: INT3 breakpoint at %s (0x%x)", t.name, t.addr)
|
||||
}
|
||||
|
||||
if runtime.GOARCH == "arm64" {
|
||||
instr := uint32(prologue[0]) | uint32(prologue[1])<<8 | uint32(prologue[2])<<16 | uint32(prologue[3])<<24
|
||||
if instr&0xFC000000 == 0x14000000 {
|
||||
return fmt.Errorf("antidebug: ARM64 B hook detected at %s (0x%x)", t.name, t.addr)
|
||||
}
|
||||
if instr&0xFFE0001F == 0xD61F0000 {
|
||||
return fmt.Errorf("antidebug: ARM64 BR hook detected at %s (0x%x)", t.name, t.addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func scanForBreakpoints() error {
|
||||
type target struct {
|
||||
name string
|
||||
addr uintptr
|
||||
}
|
||||
|
||||
targets := []target{
|
||||
{"os.Exit", reflect.ValueOf(os.Exit).Pointer()},
|
||||
{"runtime.Goexit", reflect.ValueOf(runtime.Goexit).Pointer()},
|
||||
{"fmt.Printf", reflect.ValueOf(fmt.Printf).Pointer()},
|
||||
{"os.ReadFile", reflect.ValueOf(os.ReadFile).Pointer()},
|
||||
}
|
||||
|
||||
for _, t := range targets {
|
||||
if t.addr == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
region := readAddr(t.addr, 64)
|
||||
if len(region) < 8 {
|
||||
continue
|
||||
}
|
||||
|
||||
maxConsecutive := 0
|
||||
consecutive := 0
|
||||
for _, b := range region {
|
||||
if b == 0xCC {
|
||||
consecutive++
|
||||
if consecutive > maxConsecutive {
|
||||
maxConsecutive = consecutive
|
||||
}
|
||||
} else {
|
||||
consecutive = 0
|
||||
}
|
||||
}
|
||||
|
||||
if maxConsecutive >= 4 {
|
||||
return fmt.Errorf("antidebug: breakpoint cluster (%d consecutive INT3) near %s at 0x%x", maxConsecutive, t.name, t.addr)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func readAddr(addr uintptr, size int) []byte {
|
||||
if size <= 0 || size > 4096 {
|
||||
return nil
|
||||
}
|
||||
buf := make([]byte, size)
|
||||
var src []byte
|
||||
sh := (*reflect.SliceHeader)(unsafe.Pointer(&src))
|
||||
sh.Data = addr
|
||||
sh.Len = size
|
||||
sh.Cap = size
|
||||
copy(buf, src)
|
||||
return buf
|
||||
}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
//go:build windows
|
||||
|
||||
package antidebug
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var procReadProcessMemory = kernel32.NewProc("ReadProcessMemory")
|
||||
|
||||
func checkFunctionPrologues() error {
|
||||
type target struct {
|
||||
name string
|
||||
addr uintptr
|
||||
}
|
||||
|
||||
targets := []target{
|
||||
{"os.Exit", reflect.ValueOf(os.Exit).Pointer()},
|
||||
{"runtime.Goexit", reflect.ValueOf(runtime.Goexit).Pointer()},
|
||||
}
|
||||
|
||||
currentProc, _ := syscall.GetCurrentProcess()
|
||||
|
||||
for _, t := range targets {
|
||||
if t.addr == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
prologue := make([]byte, 8)
|
||||
var bytesRead uintptr
|
||||
ret, _, _ := procReadProcessMemory.Call(
|
||||
uintptr(currentProc),
|
||||
t.addr,
|
||||
uintptr(unsafe.Pointer(&prologue[0])),
|
||||
uintptr(len(prologue)),
|
||||
uintptr(unsafe.Pointer(&bytesRead)),
|
||||
)
|
||||
if ret == 0 || bytesRead < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
if prologue[0] == 0xE9 {
|
||||
return fmt.Errorf("antidebug: JMP hook detected at %s (0x%x)", t.name, t.addr)
|
||||
}
|
||||
if prologue[0] == 0xEB {
|
||||
return fmt.Errorf("antidebug: short JMP hook detected at %s (0x%x)", t.name, t.addr)
|
||||
}
|
||||
if prologue[0] == 0xFF && prologue[1] == 0x25 {
|
||||
return fmt.Errorf("antidebug: indirect JMP hook detected at %s (0x%x)", t.name, t.addr)
|
||||
}
|
||||
if prologue[0] == 0xCC && prologue[1] == 0xCC {
|
||||
return fmt.Errorf("antidebug: INT3 breakpoint at %s (0x%x)", t.name, t.addr)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func scanForBreakpoints() error {
|
||||
type target struct {
|
||||
name string
|
||||
addr uintptr
|
||||
}
|
||||
|
||||
targets := []target{
|
||||
{"os.Exit", reflect.ValueOf(os.Exit).Pointer()},
|
||||
{"runtime.Goexit", reflect.ValueOf(runtime.Goexit).Pointer()},
|
||||
{"fmt.Printf", reflect.ValueOf(fmt.Printf).Pointer()},
|
||||
{"os.ReadFile", reflect.ValueOf(os.ReadFile).Pointer()},
|
||||
}
|
||||
|
||||
currentProc, _ := syscall.GetCurrentProcess()
|
||||
|
||||
for _, t := range targets {
|
||||
if t.addr == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
region := make([]byte, 64)
|
||||
var bytesRead uintptr
|
||||
ret, _, _ := procReadProcessMemory.Call(
|
||||
uintptr(currentProc),
|
||||
t.addr,
|
||||
uintptr(unsafe.Pointer(®ion[0])),
|
||||
uintptr(len(region)),
|
||||
uintptr(unsafe.Pointer(&bytesRead)),
|
||||
)
|
||||
if ret == 0 || bytesRead < 8 {
|
||||
continue
|
||||
}
|
||||
|
||||
maxConsecutive := 0
|
||||
consecutive := 0
|
||||
for _, b := range region[:bytesRead] {
|
||||
if b == 0xCC {
|
||||
consecutive++
|
||||
if consecutive > maxConsecutive {
|
||||
maxConsecutive = consecutive
|
||||
}
|
||||
} else {
|
||||
consecutive = 0
|
||||
}
|
||||
}
|
||||
|
||||
if maxConsecutive >= 4 {
|
||||
return fmt.Errorf("antidebug: breakpoint cluster (%d consecutive INT3) near %s at 0x%x", maxConsecutive, t.name, t.addr)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
//go:build linux
|
||||
|
||||
package antidebug
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
func checkPtrace() error {
|
||||
self, err := os.ReadFile("/proc/self/status")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
data := string(self)
|
||||
for i := 0; i < len(data)-11; i++ {
|
||||
if data[i:i+11] == "TracerPid:\t" {
|
||||
j := i + 11
|
||||
for j < len(data) && data[j] >= '0' && data[j] <= '9' {
|
||||
j++
|
||||
}
|
||||
pid := data[i+11 : j]
|
||||
if pid != "0" {
|
||||
return fmt.Errorf("antidebug: debugger detected (tracer pid=%s)", pid)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func disableCoreDump() error {
|
||||
f, err := os.OpenFile("/proc/self/coredump_filter", os.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer f.Close()
|
||||
_, _ = f.WriteString("0")
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkTiming() error {
|
||||
start := time.Now()
|
||||
sum := 0
|
||||
for i := 0; i < 1000000; i++ {
|
||||
sum += i
|
||||
}
|
||||
elapsed := time.Since(start)
|
||||
|
||||
if elapsed > 200*time.Millisecond {
|
||||
return fmt.Errorf("antidebug: timing anomaly detected (%v for 1M iterations)", elapsed)
|
||||
}
|
||||
|
||||
_ = sum
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkEnv() error {
|
||||
suspiciousVars := []string{
|
||||
"GODEBUG",
|
||||
"DELVE_LISTENER",
|
||||
"DEBUGINFOD_URL",
|
||||
"RR_LOG_FILE",
|
||||
"_JAVA_OPTIONS",
|
||||
"LD_PRELOAD",
|
||||
}
|
||||
|
||||
for _, v := range suspiciousVars {
|
||||
if os.Getenv(v) != "" {
|
||||
return fmt.Errorf("antidebug: suspicious env var %s=%s", v, os.Getenv(v))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkParentProcess() error {
|
||||
ppid := os.Getppid()
|
||||
if ppid <= 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
commPath := fmt.Sprintf("/proc/%d/comm", ppid)
|
||||
comm, err := os.ReadFile(commPath)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
parent := string(comm)
|
||||
debuggers := []string{
|
||||
"gdb", "lldb", "strace", "ltrace", "ida", "ida64",
|
||||
"radare2", "r2", "ghidra", "x64dbg", "ollydbg",
|
||||
"delve", "debugserver",
|
||||
}
|
||||
|
||||
for _, d := range debuggers {
|
||||
if len(parent) >= len(d) && parent[:len(d)] == d {
|
||||
return fmt.Errorf("antidebug: parent process is debugger: %s", parent)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func AntiAttach() {
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
if err := checkPtrace(); err != nil {
|
||||
os.Exit(66)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func AntiDump() {
|
||||
go func() {
|
||||
ticker := time.NewTicker(2 * time.Second)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
_ = disableCoreDump()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func CheckHooks() error {
|
||||
if err := checkFunctionPrologues(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := scanForBreakpoints(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
//go:build windows
|
||||
|
||||
package antidebug
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
ntdll = syscall.NewLazyDLL("ntdll.dll")
|
||||
procIsDebuggerPresent = kernel32.NewProc("IsDebuggerPresent")
|
||||
procCheckRemoteDebugger = kernel32.NewProc("CheckRemoteDebuggerPresent")
|
||||
procNtQueryInformationProc = ntdll.NewProc("NtQueryInformationProcess")
|
||||
procNtSetInformationThread = ntdll.NewProc("NtSetInformationThread")
|
||||
procOutputDebugStringW = kernel32.NewProc("OutputDebugStringW")
|
||||
procSetUnhandledException = kernel32.NewProc("SetUnhandledExceptionFilter")
|
||||
)
|
||||
|
||||
const (
|
||||
processDebugPort = 7
|
||||
processDebugObjectHandle = 0x1E
|
||||
processDebugFlags = 0x1F
|
||||
threadHideFromDebugger = 0x11
|
||||
)
|
||||
|
||||
func checkPtrace() error {
|
||||
ret, _, _ := procIsDebuggerPresent.Call()
|
||||
if ret != 0 {
|
||||
return fmt.Errorf("antidebug: IsDebuggerPresent returned true")
|
||||
}
|
||||
|
||||
var isRemoteDebugged int32
|
||||
currentProc, _ := syscall.GetCurrentProcess()
|
||||
ret, _, _ = procCheckRemoteDebugger.Call(
|
||||
uintptr(currentProc),
|
||||
uintptr(unsafe.Pointer(&isRemoteDebugged)),
|
||||
)
|
||||
if ret != 0 && isRemoteDebugged != 0 {
|
||||
return fmt.Errorf("antidebug: CheckRemoteDebuggerPresent detected debugger")
|
||||
}
|
||||
|
||||
var port uint32
|
||||
var returnLength uint32
|
||||
ret, _, _ = procNtQueryInformationProc.Call(
|
||||
uintptr(currentProc),
|
||||
processDebugPort,
|
||||
uintptr(unsafe.Pointer(&port)),
|
||||
unsafe.Sizeof(port),
|
||||
uintptr(unsafe.Pointer(&returnLength)),
|
||||
)
|
||||
if ret == 0 && port != 0 {
|
||||
return fmt.Errorf("antidebug: NtQueryInformationProcess(ProcessDebugPort) detected debugger (port=%d)", port)
|
||||
}
|
||||
|
||||
var debugHandle uintptr
|
||||
ret, _, _ = procNtQueryInformationProc.Call(
|
||||
uintptr(currentProc),
|
||||
processDebugObjectHandle,
|
||||
uintptr(unsafe.Pointer(&debugHandle)),
|
||||
unsafe.Sizeof(debugHandle),
|
||||
uintptr(unsafe.Pointer(&returnLength)),
|
||||
)
|
||||
if ret == 0 {
|
||||
return fmt.Errorf("antidebug: NtQueryInformationProcess(ProcessDebugObjectHandle) detected debugger")
|
||||
}
|
||||
|
||||
var debugFlags uint32
|
||||
ret, _, _ = procNtQueryInformationProc.Call(
|
||||
uintptr(currentProc),
|
||||
processDebugFlags,
|
||||
uintptr(unsafe.Pointer(&debugFlags)),
|
||||
unsafe.Sizeof(debugFlags),
|
||||
uintptr(unsafe.Pointer(&returnLength)),
|
||||
)
|
||||
if ret == 0 && debugFlags == 0 {
|
||||
return fmt.Errorf("antidebug: NtQueryInformationProcess(ProcessDebugFlags) detected debugger")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkPEB() error {
|
||||
var pebBase uintptr
|
||||
var returnLength uint32
|
||||
currentProc, _ := syscall.GetCurrentProcess()
|
||||
|
||||
type processBasicInfo struct {
|
||||
Reserved1 uintptr
|
||||
PebBaseAddress uintptr
|
||||
Reserved2 [2]uintptr
|
||||
UniqueProcessID uintptr
|
||||
Reserved3 uintptr
|
||||
}
|
||||
|
||||
var info processBasicInfo
|
||||
ret, _, _ := procNtQueryInformationProc.Call(
|
||||
uintptr(currentProc),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&info)),
|
||||
unsafe.Sizeof(info),
|
||||
uintptr(unsafe.Pointer(&returnLength)),
|
||||
)
|
||||
if ret != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
pebBase = info.PebBaseAddress
|
||||
if pebBase == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
beingDebugged := *(*byte)(unsafe.Pointer(pebBase + 2))
|
||||
if beingDebugged != 0 {
|
||||
return fmt.Errorf("antidebug: PEB.BeingDebugged is set")
|
||||
}
|
||||
|
||||
ntGlobalFlag := *(*uint32)(unsafe.Pointer(pebBase + 0xBC))
|
||||
const FLG_HEAP_ENABLE_TAIL_CHECK = 0x10
|
||||
const FLG_HEAP_ENABLE_FREE_CHECK = 0x20
|
||||
const FLG_HEAP_VALIDATE_PARAMETERS = 0x40
|
||||
debugFlags := FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_ENABLE_FREE_CHECK | FLG_HEAP_VALIDATE_PARAMETERS
|
||||
if ntGlobalFlag&uint32(debugFlags) != 0 {
|
||||
return fmt.Errorf("antidebug: PEB.NtGlobalFlag has debug flags (0x%x)", ntGlobalFlag)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkHardwareBreakpoints() error {
|
||||
type context struct {
|
||||
ContextFlags uint32
|
||||
Dr0 uintptr
|
||||
Dr1 uintptr
|
||||
Dr2 uintptr
|
||||
Dr3 uintptr
|
||||
Dr6 uintptr
|
||||
Dr7 uintptr
|
||||
FloatSave [112]byte
|
||||
SegGs uint32
|
||||
SegFs uint32
|
||||
SegEs uint32
|
||||
SegDs uint32
|
||||
Edi uintptr
|
||||
Esi uintptr
|
||||
Ebx uintptr
|
||||
Edx uintptr
|
||||
Ecx uintptr
|
||||
Eax uintptr
|
||||
Ebp uintptr
|
||||
Eip uintptr
|
||||
SegCs uint32
|
||||
EFlags uint32
|
||||
Esp uintptr
|
||||
SegSs uint32
|
||||
ExtendedRegisters [512]byte
|
||||
}
|
||||
|
||||
threadHandle, _ := syscall.GetCurrentThread()
|
||||
|
||||
var ctx context
|
||||
ctx.ContextFlags = 0x00100010
|
||||
|
||||
procGetThreadContext := kernel32.NewProc("GetThreadContext")
|
||||
ret, _, _ := procGetThreadContext.Call(
|
||||
uintptr(threadHandle),
|
||||
uintptr(unsafe.Pointer(&ctx)),
|
||||
)
|
||||
if ret == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if ctx.Dr0 != 0 || ctx.Dr1 != 0 || ctx.Dr2 != 0 || ctx.Dr3 != 0 {
|
||||
return fmt.Errorf("antidebug: hardware breakpoints detected (dr0=0x%x dr1=0x%x dr2=0x%x dr3=0x%x)",
|
||||
ctx.Dr0, ctx.Dr1, ctx.Dr2, ctx.Dr3)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func hideFromDebugger() {
|
||||
threadHandle, _ := syscall.GetCurrentThread()
|
||||
procNtSetInformationThread.Call(
|
||||
uintptr(threadHandle),
|
||||
threadHideFromDebugger,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
func disableCoreDump() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkTiming() error {
|
||||
start := time.Now()
|
||||
sum := 0
|
||||
for i := 0; i < 1000000; i++ {
|
||||
sum += i
|
||||
}
|
||||
elapsed := time.Since(start)
|
||||
|
||||
if elapsed > 200*time.Millisecond {
|
||||
return fmt.Errorf("antidebug: timing anomaly detected (%v for 1M iterations)", elapsed)
|
||||
}
|
||||
|
||||
_ = sum
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkEnv() error {
|
||||
suspiciousVars := []string{
|
||||
"GODEBUG",
|
||||
"DELVE_LISTENER",
|
||||
"DEBUGINFOD_URL",
|
||||
"RR_LOG_FILE",
|
||||
"_JAVA_OPTIONS",
|
||||
}
|
||||
|
||||
for _, v := range suspiciousVars {
|
||||
if os.Getenv(v) != "" {
|
||||
return fmt.Errorf("antidebug: suspicious env var %s=%s", v, os.Getenv(v))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkParentProcess() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func AntiAttach() {
|
||||
go func() {
|
||||
hideFromDebugger()
|
||||
for {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
if err := checkPtrace(); err != nil {
|
||||
os.Exit(66)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func AntiDump() {
|
||||
go func() {
|
||||
ticker := time.NewTicker(2 * time.Second)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
_ = disableCoreDump()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func CheckHooks() error {
|
||||
if err := checkFunctionPrologues(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := scanForBreakpoints(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user