Patch all Go functions for testing

Overview

SuperMonkey

gopher

This lib is inspired by https://github.com/bouk/monkey, and uses some of the code

Introduction

Patch all functions without limits, including which are unexported

Warning : please add -l to your gcflags or add //go:noinline to func which you want to patch.

when running in tests

you should run this lib under a go mod project and provide the full project path

Warning : use go test -ldflags="-s=false" -gcflags="-l" to enable symbol table and disable inline.

when running not in tests

patch private function

normal

package main

import (
	"fmt"

	sm "github.com/cch123/supermonkey"
)

func main() {
	fmt.Println("original function output:")
	heyHey()

	patchGuard := sm.Patch(heyHey, func() {
		fmt.Println("please be polite")
	})
	fmt.Println("after patch, function output:")
	heyHey()

	patchGuard.Unpatch()
	fmt.Println("unpatch, then output:")
	heyHey()
}

//go:noinline
func heyHey() {
	fmt.Println("fake")
}

go run -gcflags="-l" yourfile.go

full symbol name

package main

import (
	"fmt"

	sm "github.com/cch123/supermonkey"
)

func main() {
	fmt.Println("original function output:")
	heyHeyHey()

	patchGuard := sm.PatchByFullSymbolName("main.heyHeyHey", func() {
		fmt.Println("please be polite")
	})
	fmt.Println("after patch, function output:")
	heyHeyHey()

	patchGuard.Unpatch()
	fmt.Println("unpatch, then output:")
	heyHeyHey()
}

//go:noinline
func heyHeyHey() {
	fmt.Println("fake")
}

go run -gcflags="-l" yourfile.go

patch private instance method

normal

package main

import (
	"fmt"

	sm "github.com/cch123/supermonkey"
)

type person struct{ name string }

//go:noinline
func (p *person) speak() {
	fmt.Println("my name is ", p.name)
}

func main() {
	var p = person{"Lance"}
	fmt.Println("original function output:")
	p.speak()

	patchGuard := sm.Patch((*person).speak, func(*person) {
        fmt.Println("we are all the same")
    })
    fmt.Println("after patch, function output:")
    p.speak()

	patchGuard.Unpatch()
	fmt.Println("unpatch, then output:")
	p.speak()
}

go run -gcflags="-l" yourfile.go

full symbol name

package main

import (
	"fmt"
	"unsafe"

	sm "github.com/cch123/supermonkey"
)

type person struct{ name string }

//go:noinline
func (p *person) speak() {
	fmt.Println("my name is ", p.name)
}

func main() {
	var p = person{"Linda"}
	fmt.Println("original function output:")
	p.speak()

	patchGuard := sm.PatchByFullSymbolName("main.(*person).speak", func(ptr uintptr) {
		p = (*person)(unsafe.Pointer(ptr))
		fmt.Println(p.name, ", we are all the same")
	})
	fmt.Println("after patch, function output:")
	p.speak()

	patchGuard.Unpatch()
	fmt.Println("unpatch, then output:")
	p.speak()
}
package main

import (
	"context"
	"fmt"

	sm "github.com/cch123/supermonkey"
)

type Bar struct {
	Name string
}

type Foo struct{}

func (*Foo) MyFunc(ctx context.Context) (*Bar, error) {
	return &Bar{"Bar"}, nil
}

func main() {
	f := &Foo{}
	fmt.Println("original function output:")
	fmt.Println(f.MyFunc(nil))

	//patchGuard := sm.PatchByFullSymbolName("main.(*Foo).MyFunc", func(_ *Foo, ctx context.Context) (*Bar, error) {
	patchGuard := sm.PatchByFullSymbolName("main.(*Foo).MyFunc", func(_ uintptr, ctx context.Context) (*Bar, error) {
		return &Bar{"Not bar"}, nil
	})

	fmt.Println("after patch, function output:")
	fmt.Println(f.MyFunc(nil))

	patchGuard.Unpatch()
	fmt.Println("unpatch, then output:")
	fmt.Println(f.MyFunc(nil))
}

go run -gcflags="-l" yourfile.go

Authors

cch123

Mutated1994

Issues
  • Failed in Test

    Failed in Test

    Hey thanks for the awesome package!

    I met some problems when using supermonkey in test.

    // main.go
    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	heyHey()
    }
    
    //go:noinline
    func heyHey() {
    	fmt.Println("fake")
    }
    
    // main_test.go
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	sm "github.com/cch123/supermonkey"
    )
    
    func Test_HeyHey(t *testing.T) {
    	fmt.Println("original function output:")
    	heyHey()
    	fmt.Println()
    
    	sm.PatchByFullSymbolName("main.heyHey", func() {
    		fmt.Println("please be polite")
    	})
    	fmt.Println("after patch, function output:")
    	heyHey()
    	fmt.Println()
    
    	sm.UnpatchAll()
    	fmt.Println("unpatch all, then output:")
    	heyHey()
    }
    

    And I run the test like

    $go test -gcflags="-l" ./
    original function output:
    fake
    
    The symbol is main.heyHey, and the patch target addr is 0, there may be 2 possible reasons
            1. the function is inlined, please add //go:noinline to function comment or add -l to gcflags
            2. your input for symbolName or pkg/obj/method is wrong, check by using go tool nm {your_bin_file}
    --- FAIL: Test_HeyHey (0.00s)
    panic:  [recovered]
            panic: 
    
    goroutine 20 [running]:
    testing.tRunner.func1.1(0x1193940, 0x1222350)
            /usr/local/Cellar/go/1.14.4/libexec/src/testing/testing.go:940 +0x2f5
    testing.tRunner.func1(0xc0000d0360)
            /usr/local/Cellar/go/1.14.4/libexec/src/testing/testing.go:943 +0x3f9
    panic(0x1193940, 0x1222350)
            /usr/local/Cellar/go/1.14.4/libexec/src/runtime/panic.go:969 +0x166
    github.com/cch123/supermonkey.PatchByFullSymbolName(0x11e749e, 0xb, 0x1191d20, 0x11f4610)
            /Users/kiyon/go/pkg/mod/github.com/cch123/[email protected]/supermonkey.go:33 +0x326
    learn-golang/supermonkey.Test_HeyHey(0xc0000d0360)
            /Users/kiyon/Code/learn/learn-golang/supermonkey/main_test.go:15 +0xac
    testing.tRunner(0xc0000d0360, 0x11f4618)
            /usr/local/Cellar/go/1.14.4/libexec/src/testing/testing.go:991 +0xdc
    created by testing.(*T).Run
            /usr/local/Cellar/go/1.14.4/libexec/src/testing/testing.go:1042 +0x357
    FAIL    learn-golang/supermonkey        0.399s
    FAIL
    

    Did I miss something?

    bug 
    opened by kiyonlin 5
  • feat: Combine SuperMonkey with Monkey.

    feat: Combine SuperMonkey with Monkey.

    Changed

    • goobj、nm、objabi、objfile、src、sys、xcoff 都移到了 internal
    • bouk/monkey 代码移入 internal/bouk
    • 兼容 bouk/monkey 原先的 patch 方法
    • 增加PatchByFullSymbolName失败后相似符号提示
    • 修改 examples
    • 更新 readme

    曹大,我又来了,这次斗胆给您提交个pr。 因为 bouk/monkey 之前大家还是使用的比较多,所以是不是可以考虑兼容一下它?加量不加价,更加美滋滋☺

    opened by Mutated1994 4
  • panic: permission denied

    panic: permission denied

    image panic: permission denied [recovered] panic: permission denied

    goroutine 162 [running]: testing.tRunner.func1.2({0x4b819e0, 0x55a90a8}) /usr/local/go/src/testing/testing.go:1209 +0x24e testing.tRunner.func1() /usr/local/go/src/testing/testing.go:1212 +0x218 panic({0x4b819e0, 0x55a90a8}) /usr/local/go/src/runtime/panic.go:1038 +0x215 github.com/cch123/supermonkey/internal/bouk.mprotectCrossPage(0x461f6e0, 0xc, 0x10) /Users/sangtianyu/go/pkg/mod/github.com/cch123/[email protected]/internal/bouk/replace_unix.go:15 +0xc5 github.com/cch123/supermonkey/internal/bouk.CopyToLocation(0x461f6e0, {0xc000651cc4, 0xc, 0xc000384108}) /Users/sangtianyu/go/pkg/mod/github.com/cch123/[email protected]/internal/bouk/replace_unix.go:26 +0x5c github.com/cch123/supermonkey/internal/bouk.ReplaceFunction(0x461f6e0, 0x4cf9f08) /Users/sangtianyu/go/pkg/mod/github.com/cch123/[email protected]/internal/bouk/replace.go:29 +0xb2 github.com/cch123/supermonkey/internal/bouk.patchValue({0x4b4e800, 0x4cf9f08, 0x0}, {0x4b4e800, 0x4cf9c18, 0x13}, 0x0) /Users/sangtianyu/go/pkg/mod/github.com/cch123/[email protected]/internal/bouk/monkey.go:96 +0x2b6 github.com/cch123/supermonkey/internal/bouk.Patch({0x4b4e800, 0x4cf9f08}, {0x4b4e800, 0x4cf9c18}) /Users/sangtianyu/go/pkg/mod/github.com/cch123/[email protected]/internal/bouk/monkey.go:50 +0x17a github.com/cch123/supermonkey.Patch({0x4b4e800, 0x4cf9f08}, {0x4b4e800, 0x4cf9c18}) /Users/sangtianyu/go/pkg/mod/github.com/cch123/[email protected]/supermonkey.go:23 +0x2d

    go env GO111MODULE="on" GOARCH="amd64" GOHOSTARCH="amd64" GOHOSTOS="darwin"

    opened by pherzheyu 1
  • Is there any way to call original function by symbol name?

    Is there any way to call original function by symbol name?

            var guard_down *sm.PatchGuard
    	download_hook := "xxx.DownloadPiece"
    	guard_down = sm.PatchByFullSymbolName(download_hook, func(ctx context.Context, pt peer.Task, request *peer.DownloadPieceRequest) (success bool) {
    		// do something
    
                    // temporary unpatch
    		guard_down.Unpatch()
    		defer guard_down.Restore()
                    
                    // use forceexport to fetch original function
    		var function func(context.Context, peer.Task, *peer.DownloadPieceRequest) (success bool)
    		err := forceexport.GetFunc(&function, download_hook)
    		if err != nil {
    			log.Panicf("connect target error: %s", err)
    		}
    
                    // call original function
    		success = function(ctx, pt, request)
    		return
    	})
    

    If the original function is unexported, and should be called inside the hooked method as above, is supermonkey provide some way to call it by symbol name rather than use forceexport?

    opened by cutecutecat 2
  • undefined: JmpToFunctionValue

    undefined: JmpToFunctionValue

    It's not working on ARM architecture

    github.com/cch123/supermonkey/internal/bouk

    ../../../../go/pkg/mod/github.com/cch123/[email protected]/internal/bouk/replace.go:24:14: undefined: JmpToFunctionValue

    help wanted 
    opened by dgofman 5
  • patch fun after how get origin fun

    patch fun after how get origin fun

    not use UnPatch

    package main
    
    import (
    	"fmt"
    	sm "github.com/cch123/supermonkey"
    	"time"
    )
    //go:noinline
    func Now() time.Time {
    	return time.Time{}
    }
    
    func main() {
    	sm.Patch(time.Now, Now)
    	fmt.Println(time.Now())
    	// how call origin Now fun
    }
    
    question 
    opened by zeromake 3
Releases(v1.0.1)
Owner
Xargin
If you don't keep moving, you'll quickly fall behind.
Xargin
siusiu (suite-suite harmonics) a suite used to manage the suite, designed to free penetration testing engineers from learning and using various security tools, reducing the time and effort spent by penetration testing engineers on installing tools, remembering how to use tools.

siusiu (suite-suite harmonics) a suite used to manage the suite, designed to free penetration testing engineers from learning and using various security tools, reducing the time and effort spent by penetration testing engineers on installing tools, remembering how to use tools.

Re 249 Jun 27, 2022
A yaml data-driven testing format together with golang testing library

Specimen Yaml-based data-driven testing Specimen is a yaml data format for data-driven testing. This enforces separation between feature being tested

Design it, Run it 0 Jan 31, 2022
Benchmarking hash functions in Go

Experiment: Benchmarks of Hash Functions in Go This repository contains a small experiment of mine, trying out different hash functions and comparing

Roman Atachiants 6 Jun 22, 2022
Benchmarking deferent Fibonacci functions and algorithms with running unit test

GoFibonacciBench Benchmarking deferent Fibonacci functions and algorithms with running unit test ... Introduction: Fibonacci numbers are special kinds

null 2 Feb 27, 2022
Snapshot - snapshot provides a set of utility functions for creating and loading snapshot files for using snapshot tests.

Snapshot - snapshot provides a set of utility functions for creating and loading snapshot files for using snapshot tests.

Daniel J. Rollins 2 Jan 27, 2022
Random - A Golang library that provides functions for generating random values

Random A Golang library that provides functions for generating random values. Us

Fatih Cetinkaya 1 Apr 14, 2022
Create all possible binaries from go files

gopher-build-all Create all possible binaries of a project in go ChangeLog 0.0.2 Flags bin-folder-name ─ With this flag you can rename the output dire

FlamesX128 3 Dec 16, 2021
Fortio load testing library, command line tool, advanced echo server and web UI in go (golang). Allows to specify a set query-per-second load and record latency histograms and other useful stats.

Fortio Fortio (Φορτίο) started as, and is, Istio's load testing tool and now graduated to be its own project. Fortio is also used by, among others, Me

Fortio (Φορτίο) 2.6k Jun 24, 2022
:exclamation:Basic Assertion Library used along side native go testing, with building blocks for custom assertions

Package assert Package assert is a Basic Assertion library used along side native go testing Installation Use go get. go get github.com/go-playground/

Go Playgound 39 Apr 17, 2022
Expressive end-to-end HTTP API testing made easy in Go

baloo Expressive and versatile end-to-end HTTP API testing made easy in Go (golang), built on top of gentleman HTTP client toolkit. Take a look to the

Tom 725 May 12, 2022
Simple Go snapshot testing

Incredibly simple Go snapshot testing: cupaloy takes a snapshot of your test output and compares it to a snapshot committed alongside your tests. If t

Bradley Kemp 217 Jun 16, 2022
Clean database for testing, inspired by database_cleaner for Ruby

DbCleaner Clean database for testing, inspired by database_cleaner for Ruby. It uses flock syscall under the hood to make sure the test can runs in pa

Scott Le 140 Jun 28, 2022
Golang HTTP client testing framework

flute Golang HTTP client testing framework Presentation https://speakerdeck.com/szksh/flute-golang-http-client-testing-framework Overview flute is the

Shunsuke Suzuki 17 Apr 19, 2022
API testing framework inspired by frisby-js

frisby REST API testing framework inspired by frisby-js, written in Go Proposals I'm starting to work on frisby again with the following ideas: Read s

_Hofstadter 272 Jun 13, 2022
Mutation testing for Go source code

go-mutesting go-mutesting is a framework for performing mutation testing on Go source code. Its main purpose is to find source code, which is not cove

Markus Zimmermann 536 Jun 22, 2022
Extremely flexible golang deep comparison, extends the go testing package and tests HTTP APIs

go-testdeep Extremely flexible golang deep comparison, extends the go testing package. Latest news Synopsis Description Installation Functions Availab

Maxime Soulé 303 Jun 19, 2022
Minimal and Beautiful Go testing framework

Goblin A Mocha like BDD testing framework written in Go that requires no additional dependencies. Requires no extensive documentation nor complicated

null 852 Jun 26, 2022
A Comprehensive Coverage Testing System for The Go Programming Language

goc 中文页 | goc is a comprehensive coverage testing system for The Go Programming Language, especially for some complex scenarios, like system testing c

Qiniu Cloud 535 Jul 1, 2022
Go testing in the browser. Integrates with `go test`. Write behavioral tests in Go.

GoConvey is awesome Go testing Welcome to GoConvey, a yummy Go testing tool for gophers. Works with go test. Use it in the terminal or browser accordi

SmartyStreets 7.3k Jun 28, 2022