diff --git a/cmd/dogefuzz/env.go b/cmd/dogefuzz/env.go index 57badd1..4dd4a40 100644 --- a/cmd/dogefuzz/env.go +++ b/cmd/dogefuzz/env.go @@ -78,6 +78,7 @@ type Env interface { DirectedGreyboxFuzzer() interfaces.Fuzzer GeneticAlgorithmFuzzer() interfaces.Fuzzer PowerSchedule() interfaces.PowerSchedule + GeneticAlgorithmPowerSchedule() interfaces.GeneticAlgorithmPowerSchedule } type env struct { @@ -129,12 +130,13 @@ type env struct { transactionsCheckerJob interfaces.CronJob transactionsTimeoutCheckerJob interfaces.CronJob - fuzzerLeader interfaces.FuzzerLeader - blackboxFuzzer interfaces.Fuzzer - greyboxFuzzer interfaces.Fuzzer - directedGreyboxFuzzer interfaces.Fuzzer - geneticAlgorithmFuzzer interfaces.Fuzzer - powerSchedule interfaces.PowerSchedule + fuzzerLeader interfaces.FuzzerLeader + blackboxFuzzer interfaces.Fuzzer + greyboxFuzzer interfaces.Fuzzer + directedGreyboxFuzzer interfaces.Fuzzer + geneticAlgorithmFuzzer interfaces.Fuzzer + powerSchedule interfaces.PowerSchedule + geneticAlgorithmPowerSchedule interfaces.GeneticAlgorithmPowerSchedule } func NewEnv(cfg *config.Config) *env { @@ -493,6 +495,13 @@ func (e *env) PowerSchedule() interfaces.PowerSchedule { return e.powerSchedule } +func (e *env) GeneticAlgorithmPowerSchedule() interfaces.GeneticAlgorithmPowerSchedule { + if e.geneticAlgorithmPowerSchedule == nil { + e.geneticAlgorithmPowerSchedule = fuzz.NewGeneticAlgorithmPowerSchedule(e) + } + return e.geneticAlgorithmPowerSchedule +} + func initLogger() (*zap.Logger, error) { // rawJSON := []byte(`{ // "level": "debug", diff --git a/fuzz/common.go b/fuzz/common.go index 3b81998..6056fd3 100644 --- a/fuzz/common.go +++ b/fuzz/common.go @@ -17,6 +17,7 @@ type env interface { DirectedGreyboxFuzzer() interfaces.Fuzzer GeneticAlgorithmFuzzer() interfaces.Fuzzer PowerSchedule() interfaces.PowerSchedule + GeneticAlgorithmPowerSchedule() interfaces.GeneticAlgorithmPowerSchedule TransactionService() interfaces.TransactionService SolidityService() interfaces.SolidityService diff --git a/fuzz/ga_power_schedule.go b/fuzz/ga_power_schedule.go new file mode 100644 index 0000000..c6466d7 --- /dev/null +++ b/fuzz/ga_power_schedule.go @@ -0,0 +1,221 @@ +package fuzz + +import ( + "math" + "strings" + + "github.com/dogefuzz/dogefuzz/config" + "github.com/dogefuzz/dogefuzz/pkg/common" + "github.com/dogefuzz/dogefuzz/pkg/interfaces" + "github.com/ethereum/go-ethereum/accounts/abi" +) + +type geneticAlgorithmPowerSchedule struct { + cfg *config.Config + transactionService interfaces.TransactionService + solidityService interfaces.SolidityService + functionService interfaces.FunctionService + contractService interfaces.ContractService +} + +func NewGeneticAlgorithmPowerSchedule(e env) *geneticAlgorithmPowerSchedule { + return &geneticAlgorithmPowerSchedule{ + cfg: e.Config(), + transactionService: e.TransactionService(), + solidityService: e.SolidityService(), + functionService: e.FunctionService(), + contractService: e.ContractService(), + } +} + +func (s *geneticAlgorithmPowerSchedule) RequestSeeds(functionId string, strategy common.PowerScheduleStrategy) ([][]interface{}, error) { + function, err := s.functionService.Get(functionId) + if err != nil { + return nil, err + } + + contract, err := s.contractService.Get(function.ContractId) + if err != nil { + return nil, err + } + + abiDefinition, err := abi.JSON(strings.NewReader(contract.AbiDefinition)) + if err != nil { + return nil, err + } + method := abiDefinition.Methods[function.Name] + + transactions, err := s.transactionService.FindDoneTransactionsByFunctionIdAndOrderByTimestamp(functionId, int64(s.cfg.FuzzerConfig.SeedsSize)*2) + if err != nil { + return nil, err + } + + orderer := buildOrderer(strategy, contract) + orderer.OrderTransactions(transactions) + + seeds := make([][]string, 0) + // it will take all seeds + for idx := 0; idx < len(transactions); idx++ { + seeds = append(seeds, transactions[idx].Inputs) + } + + // select seeds to crossover + var selectedSeeds [][]string + for range seeds { + selectedSeeds = append(selectedSeeds, rouletteWheelSelection(seeds)) + } + + // do crossover + var crossoverSeeds [][]string + if len(selectedSeeds) > 2 { + for i := 0; i < s.cfg.FuzzerConfig.SeedsSize*2; i++ { + seed1, seed2 := crossover(chooseSeedsToCrossover(selectedSeeds)) + crossoverSeeds = append(crossoverSeeds, seed1) + crossoverSeeds = append(crossoverSeeds, seed2) + } + } + + deserializedSeeds, err := s.deserializeSeedsList(method, crossoverSeeds) + if err != nil { + return nil, err + } + + if len(crossoverSeeds) < s.cfg.FuzzerConfig.SeedsSize { + deserializedSeeds, err = s.completeSeedsWithPreConfiguredSeeds(method, deserializedSeeds, s.cfg.FuzzerConfig.SeedsSize-len(crossoverSeeds)) + if err != nil { + return nil, err + } + } + + return deserializedSeeds, nil +} + +func (s *geneticAlgorithmPowerSchedule) completeSeedsWithPreConfiguredSeeds(method abi.Method, seeds [][]interface{}, seedsAmountToBeAdded int) ([][]interface{}, error) { + result := make([][]interface{}, len(seeds)+seedsAmountToBeAdded) + copy(result, seeds) + for icr := 0; icr < int(seedsAmountToBeAdded); icr++ { + functionSeeds := make([]interface{}, len(method.Inputs)) + for inputsIdx, input := range method.Inputs { + handler, err := s.solidityService.GetTypeHandlerWithContext(input.Type) + if err != nil { + return nil, err + } + + err = handler.LoadSeedsAndChooseOneRandomly(s.cfg.FuzzerConfig.Seeds) + if err != nil { + return nil, err + } + + functionSeeds[inputsIdx] = handler.GetValue() + } + result[icr+len(seeds)] = functionSeeds + } + return result, nil +} + +func (s *geneticAlgorithmPowerSchedule) deserializeSeedsList(method abi.Method, seedsList [][]string) ([][]interface{}, error) { + result := make([][]interface{}, len(seedsList)) + for seedsListIdx, seeds := range seedsList { + deserializedSeeds := make([]interface{}, len(seeds)) + for inputsIdx, inputDefinition := range method.Inputs { + if len(seeds) <= inputsIdx { + return nil, ErrSeedsListInvalid + } + + handler, err := s.solidityService.GetTypeHandlerWithContext(inputDefinition.Type) + if err != nil { + return nil, err + } + + err = handler.Deserialize(seeds[inputsIdx]) + if err != nil { + return nil, err + } + deserializedSeeds[inputsIdx] = handler.GetValue() + } + result[seedsListIdx] = deserializedSeeds + } + return result, nil +} + +func rouletteWheelSelection(seedsList [][]string) []string { + rnd := common.RandomFloat64() + lengthList := len(seedsList) + var seedsSlice [][]string + + if rnd >= 0 && rnd < FIRST_INTERVAL { + seedsSlice = seedsList[0:maxIndex(lengthList, FIRST_RANGE)] + } else if rnd >= FIRST_INTERVAL && rnd < SECOND_INTERVAL { + seedsSlice = seedsList[minIndex(lengthList, FIRST_RANGE):maxIndex(lengthList, SECOND_RANGE)] + } else { + // [minIndex:len(seedsList)] + seedsSlice = seedsList[minIndex(lengthList, SECOND_RANGE):] + } + + return common.RandomChoice(seedsSlice) +} + +func chooseSeedsToCrossover(seedsList [][]string) ([]string, []string) { + return common.RandomChoice(seedsList), common.RandomChoice(seedsList) +} + +// metodos a serem testados podem ter mais de um parametro de entrada +// uma seed representa uma lista de paramentros de entrada +func crossover(seed1, seed2 []string) ([]string, []string) { + var crossoverSeed1 []string + var crossoverSeed2 []string + + if len(seed1) == len(seed2) { + for i := range seed1 { + smallest := smallestSize(seed1[i], seed2[i]) + // analisar a necessidade desta verificacao + if smallest == 0 { + crossoverSeed1 = seed1 + crossoverSeed2 = seed2 + // funcionando como esperado + } else if smallest == 1 { + var tempSeed1 string + var tempSeed2 string + for j := 0; j < smallest; j++ { + tempSeed1 = seed1[i][j:j+1] + seed2[i][j:j+1] + tempSeed2 = seed2[i][j:j+1] + seed1[i][j:j+1] + } + crossoverSeed1 = append(crossoverSeed1, tempSeed1) + crossoverSeed2 = append(crossoverSeed2, tempSeed2) + } else { + var tempSeed1 string + var tempSeed2 string + for j := 0; j < smallest; j++ { + + if j%2 == 0 { + tempSeed1 += seed2[i][j : j+1] + tempSeed2 += seed1[i][j : j+1] + } else { + tempSeed1 += seed1[i][j : j+1] + tempSeed2 += seed2[i][j : j+1] + } + } + crossoverSeed1 = append(crossoverSeed1, tempSeed1) + crossoverSeed2 = append(crossoverSeed2, tempSeed2) + } + } + } + + return crossoverSeed1, crossoverSeed2 +} + +func smallestSize(seed1, seed2 string) int { + if len(seed1) >= len(seed2) { + return len(seed2) + } + + return len(seed1) +} + +func maxIndex(lengthList int, factor float64) int { + return int(math.Ceil(float64(lengthList) * factor)) +} + +func minIndex(lengthList int, factor float64) int { + return int(math.Floor(float64(lengthList) * factor)) +} diff --git a/fuzz/genetic_algorithm.go b/fuzz/genetic_algorithm.go index 5523613..2547221 100644 --- a/fuzz/genetic_algorithm.go +++ b/fuzz/genetic_algorithm.go @@ -1,15 +1,15 @@ package fuzz import ( - "math/rand" "strings" - "time" "github.com/dogefuzz/dogefuzz/pkg/common" "github.com/dogefuzz/dogefuzz/pkg/interfaces" "github.com/ethereum/go-ethereum/accounts/abi" ) +const MUTATION_CHANCE float64 = 0.1 + // consts to define what interval of the seeds slice will selected const FIRST_INTERVAL float64 = 0.6 const SECOND_INTERVAL float64 = 0.8 @@ -19,18 +19,18 @@ const FIRST_RANGE float64 = 0.4 const SECOND_RANGE float64 = 0.7 type geneticAlgorithmFuzzer struct { - powerSchedule interfaces.PowerSchedule - solidityService interfaces.SolidityService - functionService interfaces.FunctionService - contractService interfaces.ContractService + geneticAlgorithmPowerSchedule interfaces.GeneticAlgorithmPowerSchedule + solidityService interfaces.SolidityService + functionService interfaces.FunctionService + contractService interfaces.ContractService } func NewGeneticAlgorithmFuzzer(e env) *geneticAlgorithmFuzzer { return &geneticAlgorithmFuzzer{ - powerSchedule: e.PowerSchedule(), - solidityService: e.SolidityService(), - functionService: e.FunctionService(), - contractService: e.ContractService(), + geneticAlgorithmPowerSchedule: e.GeneticAlgorithmPowerSchedule(), + solidityService: e.SolidityService(), + functionService: e.FunctionService(), + contractService: e.ContractService(), } } @@ -51,49 +51,32 @@ func (f *geneticAlgorithmFuzzer) GenerateInput(functionId string) ([]interface{} } method := abiDefinition.Methods[function.Name] - seedsList, err := f.powerSchedule.RequestSeeds(functionId, common.DISTANCE_COVERAGE_BASED_STRATEGY) + // evaluate seeds - order a list by strategy + seedsList, err := f.geneticAlgorithmPowerSchedule.RequestSeeds(functionId, common.DISTANCE_COVERAGE_BASED_STRATEGY) if err != nil { return nil, err } - var newSeedsList [][]interface{} - for range seedsList { - newSeedsList = append(newSeedsList, rouletteWheelSelection(seedsList)) - } - - chosenSeeds := common.RandomChoice(newSeedsList) + chosenSeeds := common.RandomChoice(seedsList) inputs := make([]interface{}, len(method.Inputs)) for inputsIdx, inputDefinition := range method.Inputs { + rnd := common.RandomFloat64() + handler, err := f.solidityService.GetTypeHandlerWithContext(inputDefinition.Type) if err != nil { return nil, err } - handler.SetValue(chosenSeeds[inputsIdx]) - mutationFunction := common.RandomChoice(handler.GetMutators()) - mutationFunction() - inputs[inputsIdx] = handler.GetValue() - } - - return inputs, nil -} - -func rouletteWheelSelection(seedsList [][]interface{}) []interface{} { - rand.Seed(time.Now().UnixNano()) - rnd := rand.Float64() - - if rnd >= 0 || rnd < FIRST_INTERVAL { - slice := seedsList[0:int(float64(len(seedsList))*FIRST_RANGE)] - return common.RandomChoice(slice) - } else if rnd >= FIRST_INTERVAL || rnd < SECOND_INTERVAL { - slice := seedsList[int(float64(len(seedsList))*FIRST_RANGE):int(float64(len(seedsList))*SECOND_RANGE)] + handler.SetValue(chosenSeeds[inputsIdx]) - return common.RandomChoice(slice) - } else { - slice := seedsList[int(float64(len(seedsList))*SECOND_RANGE):] + if rnd >= 0 && rnd < MUTATION_CHANCE { + mutationFunction := common.RandomChoice(handler.GetMutators()) + mutationFunction() + } - return common.RandomChoice(slice) + inputs[inputsIdx] = handler.GetValue() } + return inputs, nil } diff --git a/listener/env.go b/listener/env.go index abf3c94..b85a727 100644 --- a/listener/env.go +++ b/listener/env.go @@ -42,4 +42,5 @@ type Env interface { DirectedGreyboxFuzzer() interfaces.Fuzzer GeneticAlgorithmFuzzer() interfaces.Fuzzer PowerSchedule() interfaces.PowerSchedule + GeneticAlgorithmPowerSchedule() interfaces.GeneticAlgorithmPowerSchedule } diff --git a/pkg/common/random.go b/pkg/common/random.go index 0b82b07..a7c5c04 100644 --- a/pkg/common/random.go +++ b/pkg/common/random.go @@ -10,3 +10,12 @@ func RandomChoice[T any](slice []T) T { rndIdx := rand.Intn(len(slice)) return slice[rndIdx] } + +func RandomFloat64() float64 { + rand.Seed(time.Now().UnixNano()) + return rand.Float64() +} + +func RandomInt(number int) int { + return rand.Intn(number) +} diff --git a/pkg/interfaces/fuzz.go b/pkg/interfaces/fuzz.go index 71820a6..0cf660a 100644 --- a/pkg/interfaces/fuzz.go +++ b/pkg/interfaces/fuzz.go @@ -12,6 +12,10 @@ type PowerSchedule interface { RequestSeeds(functionId string, strategy common.PowerScheduleStrategy) ([][]interface{}, error) } +type GeneticAlgorithmPowerSchedule interface { + RequestSeeds(functionId string, strategy common.PowerScheduleStrategy) ([][]interface{}, error) +} + type Fuzzer interface { GenerateInput(functionId string) ([]interface{}, error) }