From 87142c0cb5f42d877327c357497a5be6f3c5dbd0 Mon Sep 17 00:00:00 2001 From: Emile Date: Sun, 9 Apr 2023 00:37:54 +0200 Subject: Should work, lol --- README.md | 46 +----- go.mod | 10 +- go.sum | 25 ++-- main.go | 463 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/cmd.go | 20 --- src/config.go | 76 ---------- src/init.go | 230 ---------------------------- src/logic.go | 17 --- src/main.go | 27 ---- src/run.go | 84 ----------- src/structs.go | 49 ------ 11 files changed, 486 insertions(+), 561 deletions(-) create mode 100644 main.go delete mode 100644 src/cmd.go delete mode 100644 src/config.go delete mode 100644 src/init.go delete mode 100644 src/logic.go delete mode 100644 src/main.go delete mode 100644 src/run.go delete mode 100644 src/structs.go diff --git a/README.md b/README.md index e2887cb..c473557 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,11 @@ -## r2wars +# r2wars-new -The goal of r2wars is to place two bots in as shared memory space and let -them battle against each other until one of them cannot run anymore, because -of some kind of broken instruction. +This is a quick and dirty implementation of r2wars for the easterhegg 20 +workshops I'm giving (It's currently 01:00 of the day of the workshop and I'm +realizing that the code I wrote about 3 years ago is kind of broken). -A more informal README can be found -[here](https://github.com/radareorg/radare2-extras/tree/master/r2wars). - -## Usage - -You'll probably first of all want to simply play with the two provided bots. In -order to do so, run the game like this: - -```go -go run ./... -t 1s -v ./bots/warrior0.asm ./bots/warrior1.asm ``` - -This runs the game with a round duration of 1 second and an info verbosity -level using the two provided bots. You can attach more bots if you'd like, -each bot increases the arena size by 512 bytes by default. - -You can tweak most of the settings as displayed in the help: - -```go -$ go run ./... -h -Usage of src: - -arch string - bot architecture (mips|arm|x86) (default "x86") - -bits int - bot bitness (8|16|32|64) (default 32) - -maxProgSize int - the maximum bot size (default 64) - -memPerBot int - the amount of memory each bot should add to the arena (default 512) - -t duration - The duration of a round (default 250ns) - -v info - -vv - debug - -vvv - trace +; CGO_ENABLED=0 go run ./... -t 1s -v ./bots/warrior0.asm ./bots/warrior1.as ``` + +#worksonmymachine diff --git a/go.mod b/go.mod index 170fcbc..e344fca 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,9 @@ -module git.darknebu.la/emile/genetic_r2_bots +module github.com/hanemile/r2wars -go 1.13 +go 1.19 require ( - github.com/radare/r2pipe-go v0.0.0-20171013021603-08f2b4f8ca0a - github.com/rainycape/dl v0.0.0-20151222075243-1b01514224a1 // indirect - github.com/sirupsen/logrus v1.4.2 + github.com/radareorg/r2pipe-go v0.2.1 // indirect + github.com/sirupsen/logrus v1.9.0 // indirect + golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect ) diff --git a/go.sum b/go.sum index ecc0ad3..0d37478 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,13 @@ -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/radare/r2pipe-go v0.0.0-20171013021603-08f2b4f8ca0a h1:enQJXC+C+uCM0rpONspYMD6+Jf6eYNj4gCD4MIXkP8c= -github.com/radare/r2pipe-go v0.0.0-20171013021603-08f2b4f8ca0a/go.mod h1:xnSZtTv/jeHkBsW7Mg6lJWB8Ke17mg3dMCMRwI16LnM= -github.com/rainycape/dl v0.0.0-20151222075243-1b01514224a1 h1:XZlja+DeIOJeEPWAfM9M5wko0keB9qgRfuuWbry6VBQ= -github.com/rainycape/dl v0.0.0-20151222075243-1b01514224a1/go.mod h1:lh74SQgfeEuNq74dKWzDLuVB+/ntX5c4g1oDyL4GkGg= -github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +github.com/radareorg/r2pipe-go v0.2.1 h1:2bVIi7qHPQCdjjngcJbQO2VHSqPaRBt5LUAMk2uq84o= +github.com/radareorg/r2pipe-go v0.2.1/go.mod h1:HNUWNjS7SjzhZ7McDLhvIBSOLz1X9DNLjj4J52pAj3Y= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..abaf5fa --- /dev/null +++ b/main.go @@ -0,0 +1,463 @@ +package main + +import ( + "flag" + "fmt" + "math/rand" + "os" + "strings" + "time" + + "github.com/radareorg/r2pipe-go" + "github.com/sirupsen/logrus" +) + +// Config defines the meta config +type Config struct { + + // Arch defines the architecture the battle should run in + Arch string + + // Bits defines the bitness + Bits int + + // Memsize defines the arena size + Memsize int + + // MaxProgSize defines the maximal bot size + MaxProgSize int + + // Bots defines a list of bots to take part in the battle + Bots []Bot + + // AmountOfBots defines the amount of bots taking part in the tournament + AmountOfBots int + + // RandomOffsets defines the offset in memory where the bots should be placed + RandomOffsets []int + + // GameRoundTime defines the length of a gameround + GameRoundDuration time.Duration +} + +// Bot defines a bot +type Bot struct { + + // Path defines the path to the source of the bot + Path string + + // Source defines the source of the bot after being compiled with rasm2 + Source string + + // Addr defines the initial address the bot is placed at + Addr int + + // Regs defines the state of the registers of the bot + // It is used to store the registers after each round and restore them in the + // next round when the bot's turn has come + Regs string +} + +func parseConfig() Config { + arch := flag.String("arch", "x86", "bot architecture (mips|arm|x86)") + bits := flag.Int("bits", 32, "bot bitness (8|16|32|64)") + maxProgSize := flag.Int("maxProgSize", 64, "the maximum bot size") + memPerBot := flag.Int("memPerBot", 512, "the amount of memory each bot should add to the arena") + gameRoundDuration := flag.Duration("t", 250*time.Millisecond, "The duration of a round") + + v := flag.Bool("v", false, "info") + vv := flag.Bool("vv", false, "debug") + vvv := flag.Bool("vvv", false, "trace") + + flag.Parse() + + if *v == true { + logrus.SetLevel(logrus.InfoLevel) + } else if *vv == true { + logrus.SetLevel(logrus.DebugLevel) + } else if *vvv == true { + logrus.SetLevel(logrus.TraceLevel) + } else { + logrus.SetLevel(logrus.WarnLevel) + } + + // parse all trailing command line arguments as path to bot sourcecode + amountOfBots := flag.NArg() + + memsize := amountOfBots * *memPerBot + + logrus.WithFields(logrus.Fields{ + "mem per bot": *memPerBot, + "amountOfBots": amountOfBots, + "memsize": memsize, + }).Infof("Loaded config") + + // define a config to return + config := Config{ + Arch: *arch, + Bits: *bits, + Memsize: memsize, + MaxProgSize: *maxProgSize, + AmountOfBots: amountOfBots, + GameRoundDuration: *gameRoundDuration, + } + + return config +} + +// define bots defines the bots given via command line arguments +func defineBots(config *Config) { + + logrus.Info("Defining the bots") + + // define a list of bots by parsing the command line arguments one by one + var bots []Bot + for i := 0; i < config.AmountOfBots; i++ { + bot := Bot{ + Path: flag.Arg(i), + } + bots = append(bots, bot) + } + + config.Bots = bots +} + +func r2cmd(r2p *r2pipe.Pipe, input string) string { + + logrus.Tracef("> %s", input) + + // send a command + buf1, err := r2p.Cmd(input) + if err != nil { + panic(err) + } + + // return the result of the command as a string + return buf1 +} + +func buildBots(config *Config) { + + logrus.Info("Building all bots") + + // build all the bots + for i := 0; i < config.AmountOfBots; i++ { + buildBot(i, config) + } +} + +// buildBot builds the bot located at the given path. +func buildBot(i int, config *Config) { + + logrus.Debugf("Building bot %d", i) + + // open radare without input for building the bot + r2p1, err := r2pipe.NewPipe("--") + if err != nil { + panic(err) + } + defer r2p1.Close() + + // Compile a warrior using rasm2 + // + // This uses the given architecture, the given bitness and the given path in + // rasm2 to compile the bot + botPath := config.Bots[i].Path + radareCommand := fmt.Sprintf("rasm2 -a %s -b %d -f %s", config.Arch, config.Bits, botPath) + botSource := r2cmd(r2p1, radareCommand) + + config.Bots[i].Source = botSource +} + +// init initializes the arena +func initArena(config *Config) *r2pipe.Pipe { + + logrus.Info("Initializing the arena") + logrus.Debugf("Allocating %d bytes of memory...", config.Memsize) + + // allocate memory + r2p, err := r2pipe.NewPipe(fmt.Sprintf("malloc://%d", config.Memsize)) + if err != nil { + panic(err) + } + + logrus.Info("Memoy successfully allocated") + + // define the architecture and the bitness + _ = r2cmd(r2p, fmt.Sprintf("e asm.arch = %s", config.Arch)) + _ = r2cmd(r2p, fmt.Sprintf("e asm.bits = %d", config.Bits)) + + // enable colors + // _ = r2cmd(r2p, "e scr.color = 0") + _ = r2cmd(r2p, "e scr.color = 3") + _ = r2cmd(r2p, "e scr.color.args = true") + _ = r2cmd(r2p, "e scr.color.bytes = true") + _ = r2cmd(r2p, "e scr.color.grep = true") + _ = r2cmd(r2p, "e scr.color.ops = true") + _ = r2cmd(r2p, "e scr.bgfill = true") + _ = r2cmd(r2p, "e scr.color.pipe = true") + _ = r2cmd(r2p, "e scr.utf8 = true") + + // hex column width + _ = r2cmd(r2p, "e hex.cols = 32") + + // initialize ESIL VM state + logrus.Debug("Initializing the ESIL VM") + _ = r2cmd(r2p, "aei") + + // initialize ESIL VM stack + logrus.Debug("Initializing the ESIL Stack") + _ = r2cmd(r2p, "aeim") + + // return the pipe + return r2p +} + +// genRandomOffsets returns random offsets for all bots +// This is used to get the offset the bots are initially placed in +func genRandomOffsets(config *Config) { + + logrus.Info("Generating random bot offsets") + + // define the amount of bots, an array to store the offsets in and a counter + // to store the amount of tries it took to find a random positon for the bots + var amountOfBots int = len(config.Bots) + var offsets []int + var roundCounter int = 0 + + // seed the random number generator + rand.Seed(time.Now().UTC().UnixNano()) + + for { + + // reset the offsets array + offsets = []int{} + + // define a random address + // | ------------------------------------- | ----- | + // | generate an address in this space + address := rand.Intn(config.Memsize - config.MaxProgSize) + logrus.Tracef("%d", address) + + // for all bots, try to generate another random address after the intially + // generated address and test if it fits in memory + for i := 0; i < amountOfBots; i++ { + + // append the address to the offsets array + offsets = append(offsets, address) + + // define a min and max bound + // + // | ------|-|----------------------------------|-| + // | | | in this space | | + // a b c d e + // + // a = 0x0 + // b = address + // c = address + config.MaxProcSize (min) + // d = config.Memsize - config.MaxProgSize (max) + // e = config.Memsize + min := address + config.MaxProgSize + max := config.Memsize - config.MaxProgSize + + // if the new minimum bound is bigger or equal to the maximum bound, + // discard this try and start with a fresh new initial address + if min >= max { + roundCounter++ + break + } + + // generate a new address in the [min, max) range defined above + address = rand.Intn(max-min) + min + logrus.Tracef("%d", address) + + // If there isn't enough space inbetween the address and the biggest + // possible address, as in, the biggest possible bot can't fit in that + // space, discard and start with a new fresh initial address + if (config.Memsize-config.MaxProgSize)-address < config.MaxProgSize { + roundCounter++ + break + } + } + + // if the needed amount of offsets has been found, break out of the infinite loop + if len(offsets) == amountOfBots { + break + } + } + + logrus.Infof("Initial bot positions found after %d tries", roundCounter) + + // debug print all offsets + var fields0 logrus.Fields = make(logrus.Fields) + for i := 0; i < len(offsets); i++ { + fields0[fmt.Sprintf("%d", i)] = offsets[i] + } + logrus.WithFields(fields0).Debug("Offsets") + + // shuffle the offsets + rand.Shuffle(len(offsets), func(i, j int) { + offsets[i], offsets[j] = offsets[j], offsets[i] + }) + + // debug print the shuffled offsets + var fields1 logrus.Fields = make(logrus.Fields) + for i := 0; i < len(offsets); i++ { + fields1[fmt.Sprintf("%d", i)] = offsets[i] + } + logrus.WithFields(fields1).Debug("Shuffled offsets") + + config.RandomOffsets = offsets +} + +// place the bot in the arena at the given address +func placeBot(r2p *r2pipe.Pipe, bot Bot, address int) { + _ = r2cmd(r2p, fmt.Sprintf("wx %s @ %d", bot.Source, address)) +} + +func placeBots(r2p *r2pipe.Pipe, config *Config) { + + logrus.Info("Placing the bots in the arena") + + // place each bot in the arena + for bot := 0; bot < len(config.Bots); bot++ { + + // get the address where the bot should be placed + address := config.RandomOffsets[bot] + + // Place the bot in the arena + logrus.Debugf("[i] Placing bot %d at %d", bot, address) + placeBot(r2p, config.Bots[bot], address) + + logrus.Debugf("\n%s", r2cmd(r2p, fmt.Sprintf("pd 0x8 @ %d", address))) + + // store the initial address of the bot in the according struct field + config.Bots[bot].Addr = address + + // define the instruction point and the stack pointer + _ = r2cmd(r2p, fmt.Sprintf("aer PC=%d", config.Bots[bot].Addr)) + _ = r2cmd(r2p, fmt.Sprintf("aer SP=SP+%d", config.Bots[bot].Addr)) + + // dump the registers of the bot for being able to switch inbetween them + // This is done in order to be able to play one step of each bot at a time, + // but sort of in parallel + initialRegisers := strings.Replace(r2cmd(r2p, "aerR"), "\n", ";", -1) + config.Bots[bot].Regs = initialRegisers + } +} + +func defineErrors(r2p *r2pipe.Pipe) { + // handle errors in esil + _ = r2cmd(r2p, "e cmd.esil.todo=f theend=1") + _ = r2cmd(r2p, "e cmd.esil.trap=f theend=1") + _ = r2cmd(r2p, "e cmd.esil.intr=f theend=1") + _ = r2cmd(r2p, "e cmd.esil.ioer=f theend=1") + _ = r2cmd(r2p, "f theend=0") +} + +// StepIn steps in and stores the state of the registers for the given bot +func stepIn(r2p *r2pipe.Pipe) { + _ = r2cmd(r2p, "aes") +} + +// switchPlayer returns the id of the next Player +func switchPlayer(r2p *r2pipe.Pipe, currentPlayer int, config *Config) int { + + // calculate the index of the nextPlayer + nextPlayer := (currentPlayer + 1) % config.AmountOfBots + + return nextPlayer +} + +func arena(r2p *r2pipe.Pipe, config *Config, id, gen int) string { + var res string = "" + + // clear the screen + res += "\x1b[2J\x1b[0;0H" + // res += fmt.Sprintf("%s\n", r2cmd(r2p, "?eg 0 0")) + + // print some general information such as the current user and the round the + // game is in + ip := fmt.Sprintf("%s\n", r2cmd(r2p, "aer~eip")) + res += fmt.Sprintf("Round: %d \t\t User: %d \t\t ip: %s\n", gen, id, ip) + + // print the memory space + res += fmt.Sprintf("%s\n", r2cmd(r2p, "pxa 0x400 @ 0")) + // res += fmt.Sprintf("%s\n", r2cmd(r2p, fmt.Sprintf("pd 0x10 @ %d", config.Bots[id].Addr))) + + // res += fmt.Sprintf("%s\n", r2cmd(r2p, "prc 0x200 @ 0")) + + return res +} + +// runGame actually runs the game (surprise!) +func runGame(r2p *r2pipe.Pipe, config *Config) { + + // start the competition + var botid int = 0 + var round int = 0 + for true { + + // load the registers + r2cmd(r2p, config.Bots[botid].Regs) + + // Step + stepIn(r2p) + + // store the regisers + registers := r2cmd(r2p, "aerR") + registersStripped := strings.Replace(registers, "\n", ";", -1) + config.Bots[botid].Regs = registersStripped + + logrus.Info(arena(r2p, config, botid, round)) + + if dead(r2p, botid) == true { + logrus.Warnf("DEAD (round %d)", round) + os.Exit(1) + } + + // switch players, if the new botid is 0, a new round has begun + botid = switchPlayer(r2p, botid, config) + if botid == 0 { + round++ + } + + // sleep only a partial of the total round time, as a round is made up of + // the movements of multiple bots + time.Sleep(config.GameRoundDuration / time.Duration(config.AmountOfBots)) + } +} + +func dead(r2p *r2pipe.Pipe, botid int) bool { + status := r2cmd(r2p, "?v 1+theend") + + if status != "" && status != "0x1" { + logrus.Warn("[!] Bot %d has died", botid) + return true + } + return false +} + +func main() { + fmt.Println("hi") + + config := parseConfig() + defineBots(&config) + buildBots(&config) + genRandomOffsets(&config) + + // initialize the arena (allocate memory + initialize the ESIL VM & stack) + r2p := initArena(&config) + + // place the bots in the arena + placeBots(r2p, &config) + + // if an error occurs (interrupt, ioerror, trap, ...), the ESIL VM should set + // a flag that can be used to determine if a player has died + defineErrors(r2p) + + // run the actual game + runGame(r2p, &config) + + r2p.Close() +} diff --git a/src/cmd.go b/src/cmd.go deleted file mode 100644 index a17da4a..0000000 --- a/src/cmd.go +++ /dev/null @@ -1,20 +0,0 @@ -package main - -import ( - "github.com/radare/r2pipe-go" - "github.com/sirupsen/logrus" -) - -func r2cmd(r2p *r2pipe.Pipe, input string) string { - - logrus.Tracef("> %s", input) - - // send a command - buf1, err := r2p.Cmd(input) - if err != nil { - panic(err) - } - - // return the result of the command as a string - return buf1 -} diff --git a/src/config.go b/src/config.go deleted file mode 100644 index 706d29e..0000000 --- a/src/config.go +++ /dev/null @@ -1,76 +0,0 @@ -package main - -import ( - "flag" - "time" - - "github.com/sirupsen/logrus" -) - -// parseConfig parses the config needed to start the game -func parseConfig() Config { - - // bot configs - arch := flag.String("arch", "x86", "bot architecture (mips|arm|x86)") - bits := flag.Int("bits", 32, "bot bitness (8|16|32|64)") - maxProgSize := flag.Int("maxProgSize", 64, "the maximum bot size") - memPerBot := flag.Int("memPerBot", 512, "the amount of memory each bot should add to the arena") - gameRoundDuration := flag.Duration("t", 250*time.Millisecond, "The duration of a round") - - v := flag.Bool("v", false, "info") - vv := flag.Bool("vv", false, "debug") - vvv := flag.Bool("vvv", false, "trace") - - // parse the flags - flag.Parse() - - if *v == true { - logrus.SetLevel(logrus.InfoLevel) - } else if *vv == true { - logrus.SetLevel(logrus.DebugLevel) - } else if *vvv == true { - logrus.SetLevel(logrus.TraceLevel) - } else { - logrus.SetLevel(logrus.WarnLevel) - } - - // parse all trailing command line arguments as path to bot sourcecode - amountOfBots := flag.NArg() - - memsize := amountOfBots * *memPerBot - - logrus.WithFields(logrus.Fields{ - "mem per bot": *memPerBot, - "amountOfBots": amountOfBots, - "memsize": memsize, - }).Infof("Loaded config") - - // define a config to return - config := Config{ - Arch: *arch, - Bits: *bits, - Memsize: memsize, - MaxProgSize: *maxProgSize, - AmountOfBots: amountOfBots, - GameRoundDuration: *gameRoundDuration, - } - - return config -} - -// define bots defines the bots given via command line arguments -func defineBots(config *Config) { - - logrus.Info("Defining the bots") - - // define a list of bots by parsing the command line arguments one by one - var bots []Bot - for i := 0; i < config.AmountOfBots; i++ { - bot := Bot{ - Path: flag.Arg(i), - } - bots = append(bots, bot) - } - - config.Bots = bots -} diff --git a/src/init.go b/src/init.go deleted file mode 100644 index ee6db87..0000000 --- a/src/init.go +++ /dev/null @@ -1,230 +0,0 @@ -package main - -import ( - "fmt" - "math/rand" - "strings" - "time" - - r2pipe "github.com/radare/r2pipe-go" - "github.com/sirupsen/logrus" -) - -func buildBots(config *Config) { - - logrus.Info("Building all bots") - - // build all the bots - for i := 0; i < config.AmountOfBots; i++ { - buildBot(i, config) - } -} - -// buildBot builds the bot located at the given path. -func buildBot(i int, config *Config) { - - logrus.Debugf("Building bot %d", i) - - // open radare without input for building the bot - r2p1, err := r2pipe.NewPipe("--") - if err != nil { - panic(err) - } - defer r2p1.Close() - - // Compile a warrior using rasm2 - // - // This uses the given architecture, the given bitness and the given path in - // rasm2 to compile the bot - botPath := config.Bots[i].Path - radareCommand := fmt.Sprintf("rasm2 -a %s -b %d -f %s", config.Arch, config.Bits, botPath) - botSource := r2cmd(r2p1, radareCommand) - - config.Bots[i].Source = botSource -} - -// init initializes the arena -func initArena(config *Config) *r2pipe.Pipe { - - logrus.Info("Initializing the arena") - logrus.Debugf("Allocating %d bytes of memory...", config.Memsize) - - // allocate memory - r2p, err := r2pipe.NewPipe(fmt.Sprintf("malloc://%d", config.Memsize)) - if err != nil { - panic(err) - } - - logrus.Info("Memoy successfully allocated") - - // define the architecture and the bitness - _ = r2cmd(r2p, fmt.Sprintf("e asm.arch = %s", config.Arch)) - _ = r2cmd(r2p, fmt.Sprintf("e asm.bits = %d", config.Bits)) - - // enable colors - // _ = r2cmd(r2p, "e scr.color = 0") - _ = r2cmd(r2p, "e scr.color = 3") - _ = r2cmd(r2p, "e scr.color.args = true") - _ = r2cmd(r2p, "e scr.color.bytes = true") - _ = r2cmd(r2p, "e scr.color.grep = true") - _ = r2cmd(r2p, "e scr.color.ops = true") - _ = r2cmd(r2p, "e scr.bgfill = true") - _ = r2cmd(r2p, "e scr.color.pipe = true") - _ = r2cmd(r2p, "e scr.utf8 = true") - - // hex column width - _ = r2cmd(r2p, "e hex.cols = 32") - - // initialize ESIL VM state - logrus.Debug("Initializing the ESIL VM") - _ = r2cmd(r2p, "aei") - - // initialize ESIL VM stack - logrus.Debug("Initializing the ESIL Stack") - _ = r2cmd(r2p, "aeim") - - // return the pipe - return r2p -} - -// genRandomOffsets returns random offsets for all bots -// This is used to get the offset the bots are initially placed in -func genRandomOffsets(config *Config) { - - logrus.Info("Generating random bot offsets") - - // define the amount of bots, an array to store the offsets in and a counter - // to store the amount of tries it took to find a random positon for the bots - var amountOfBots int = len(config.Bots) - var offsets []int - var roundCounter int = 0 - - // seed the random number generator - rand.Seed(time.Now().UTC().UnixNano()) - - for { - - // reset the offsets array - offsets = []int{} - - // define a random address - // | ------------------------------------- | ----- | - // | generate an address in this space - address := rand.Intn(config.Memsize - config.MaxProgSize) - logrus.Tracef("%d", address) - - // for all bots, try to generate another random address after the intially - // generated address and test if it fits in memory - for i := 0; i < amountOfBots; i++ { - - // append the address to the offsets array - offsets = append(offsets, address) - - // define a min and max bound - // - // | ------|-|----------------------------------|-| - // | | | in this space | | - // a b c d e - // - // a = 0x0 - // b = address - // c = address + config.MaxProcSize (min) - // d = config.Memsize - config.MaxProgSize (max) - // e = config.Memsize - min := address + config.MaxProgSize - max := config.Memsize - config.MaxProgSize - - // if the new minimum bound is bigger or equal to the maximum bound, - // discard this try and start with a fresh new initial address - if min >= max { - roundCounter++ - break - } - - // generate a new address in the [min, max) range defined above - address = rand.Intn(max-min) + min - logrus.Tracef("%d", address) - - // If there isn't enough space inbetween the address and the biggest - // possible address, as in, the biggest possible bot can't fit in that - // space, discard and start with a new fresh initial address - if (config.Memsize-config.MaxProgSize)-address < config.MaxProgSize { - roundCounter++ - break - } - } - - // if the needed amount of offsets has been found, break out of the infinite loop - if len(offsets) == amountOfBots { - break - } - } - - logrus.Infof("Initial bot positions found after %d tries", roundCounter) - - // debug print all offsets - var fields0 logrus.Fields = make(logrus.Fields) - for i := 0; i < len(offsets); i++ { - fields0[fmt.Sprintf("%d", i)] = offsets[i] - } - logrus.WithFields(fields0).Debug("Offsets") - - // shuffle the offsets - rand.Shuffle(len(offsets), func(i, j int) { - offsets[i], offsets[j] = offsets[j], offsets[i] - }) - - // debug print the shuffled offsets - var fields1 logrus.Fields = make(logrus.Fields) - for i := 0; i < len(offsets); i++ { - fields1[fmt.Sprintf("%d", i)] = offsets[i] - } - logrus.WithFields(fields1).Debug("Shuffled offsets") - - config.RandomOffsets = offsets -} - -// place the bot in the arena at the given address -func placeBot(r2p *r2pipe.Pipe, bot Bot, address int) { - _ = r2cmd(r2p, fmt.Sprintf("wx %s @ %d", bot.Source, address)) -} - -func placeBots(r2p *r2pipe.Pipe, config *Config) { - - logrus.Info("Placing the bots in the arena") - - // place each bot in the arena - for bot := 0; bot < len(config.Bots); bot++ { - - // get the address where the bot should be placed - address := config.RandomOffsets[bot] - - // Place the bot in the arena - logrus.Debugf("[i] Placing bot %d at %d", bot, address) - placeBot(r2p, config.Bots[bot], address) - - logrus.Debugf("\n%s", r2cmd(r2p, fmt.Sprintf("pd 0x8 @ %d", address))) - - // store the initial address of the bot in the according struct field - config.Bots[bot].Addr = address - - // define the instruction point and the stack pointer - _ = r2cmd(r2p, fmt.Sprintf("aer PC=%d", config.Bots[bot].Addr)) - _ = r2cmd(r2p, fmt.Sprintf("aer SP=SP+%d", config.Bots[bot].Addr)) - - // dump the registers of the bot for being able to switch inbetween them - // This is done in order to be able to play one step of each bot at a time, - // but sort of in parallel - initialRegisers := strings.Replace(r2cmd(r2p, "aerR"), "\n", ";", -1) - config.Bots[bot].Regs = initialRegisers - } -} - -func defineErrors(r2p *r2pipe.Pipe) { - // handle errors in esil - _ = r2cmd(r2p, "e cmd.esil.todo=f theend=1") - _ = r2cmd(r2p, "e cmd.esil.trap=f theend=1") - _ = r2cmd(r2p, "e cmd.esil.intr=f theend=1") - _ = r2cmd(r2p, "e cmd.esil.ioer=f theend=1") - _ = r2cmd(r2p, "f theend=0") -} diff --git a/src/logic.go b/src/logic.go deleted file mode 100644 index 5bc6461..0000000 --- a/src/logic.go +++ /dev/null @@ -1,17 +0,0 @@ -package main - -import ( - "log" - - "github.com/radare/r2pipe-go" -) - -func dead(r2p *r2pipe.Pipe, botid int) bool { - status := r2cmd(r2p, "?v 1+theend") - - if status != "" && status != "0x1" { - log.Printf("[!] Bot %d has died", botid) - return true - } - return false -} diff --git a/src/main.go b/src/main.go deleted file mode 100644 index d5e0107..0000000 --- a/src/main.go +++ /dev/null @@ -1,27 +0,0 @@ -package main - -func main() { - - // initialize the game by parsing the config, defining the bots, building the - // bots and generating random offsets where the bots should be placed in - // memory - config := parseConfig() - defineBots(&config) - buildBots(&config) - genRandomOffsets(&config) - - // initialize the arena (allocate memory + initialize the ESIL VM & stack) - r2p := initArena(&config) - - // place the bots in the arena - placeBots(r2p, &config) - - // if an error occurs (interrupt, ioerror, trap, ...), the ESIL VM should set - // a flag that can be used to determine if a player has died - defineErrors(r2p) - - // run the actual game - runGame(r2p, &config) - - r2p.Close() -} diff --git a/src/run.go b/src/run.go deleted file mode 100644 index fb3c249..0000000 --- a/src/run.go +++ /dev/null @@ -1,84 +0,0 @@ -package main - -import ( - "fmt" - "os" - "strings" - "time" - - "github.com/radare/r2pipe-go" - "github.com/sirupsen/logrus" -) - -// StepIn steps in and stores the state of the registers for the given bot -func stepIn(r2p *r2pipe.Pipe) { - _ = r2cmd(r2p, "aes") -} - -// switchPlayer returns the id of the next Player -func switchPlayer(r2p *r2pipe.Pipe, currentPlayer int, config *Config) int { - - // calculate the index of the nextPlayer - nextPlayer := (currentPlayer + 1) % config.AmountOfBots - - return nextPlayer -} - -func arena(r2p *r2pipe.Pipe, config *Config, id, gen int) string { - var res string = "" - - // clear the screen - res += "\x1b[2J\x1b[0;0H" - // res += fmt.Sprintf("%s\n", r2cmd(r2p, "?eg 0 0")) - - // print some general information such as the current user and the round the - // game is in - ip := fmt.Sprintf("%s\n", r2cmd(r2p, "aer~eip")) - res += fmt.Sprintf("Round: %d \t\t User: %d \t\t ip: %s\n", gen, id, ip) - - // print the memory space - res += fmt.Sprintf("%s\n", r2cmd(r2p, "pxa 0x400 @ 0")) - // res += fmt.Sprintf("%s\n", r2cmd(r2p, fmt.Sprintf("pd 0x10 @ %d", config.Bots[id].Addr))) - - // res += fmt.Sprintf("%s\n", r2cmd(r2p, "prc 0x200 @ 0")) - - return res -} - -// runGame actually runs the game (surprise!) -func runGame(r2p *r2pipe.Pipe, config *Config) { - - // start the competition - var botid int = 0 - var round int = 0 - for true { - - // load the registers - r2cmd(r2p, config.Bots[botid].Regs) - - // Step - stepIn(r2p) - - // store the regisers - registers := r2cmd(r2p, "aerR") - registersStripped := strings.Replace(registers, "\n", ";", -1) - config.Bots[botid].Regs = registersStripped - - logrus.Info(arena(r2p, config, botid, round)) - - if dead(r2p, botid) == true { - logrus.Warnf("DEAD (round %d)", round) - os.Exit(1) - } - - // switch players, if the new botid is 0, a new round has begun - botid = switchPlayer(r2p, botid, config) - if botid == 0 { - round++ - } - - // sleep only a partial of the total round time, as a round is made up of - // the movements of multiple bots - time.Sleep(config.GameRoundDuration / time.Duration(config.AmountOfBots)) - } -} diff --git a/src/structs.go b/src/structs.go deleted file mode 100644 index c0cddc2..0000000 --- a/src/structs.go +++ /dev/null @@ -1,49 +0,0 @@ -package main - -import "time" - -// Config defines the meta config -type Config struct { - - // Arch defines the architecture the battle should run in - Arch string - - // Bits defines the bitness - Bits int - - // Memsize defines the arena size - Memsize int - - // MaxProgSize defines the maximal bot size - MaxProgSize int - - // Bots defines a list of bots to take part in the battle - Bots []Bot - - // AmountOfBots defines the amount of bots taking part in the tournament - AmountOfBots int - - // RandomOffsets defines the offset in memory where the bots should be placed - RandomOffsets []int - - // GameRoundTime defines the length of a gameround - GameRoundDuration time.Duration -} - -// Bot defines a bot -type Bot struct { - - // Path defines the path to the source of the bot - Path string - - // Source defines the source of the bot after being compiled with rasm2 - Source string - - // Addr defines the initial address the bot is placed at - Addr int - - // Regs defines the state of the registers of the bot - // It is used to store the registers after each round and restore them in the - // next round when the bot's turn has come - Regs string -} -- cgit 1.4.1