├── go.mod ├── README.md ├── LICENSE └── main.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mwinters0/xpow 2 | 3 | go 1.23 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xpow 2 | A combination of the [hashcat utils](https://hashcat.net/wiki/doku.php?id=hashcat_utils) `combinatorX` and `combipow`. 3 | 4 | Creates all permutations of multiple lists, while using only one item from each list. 5 | 6 | ## install 7 | Grab a binary from the [releases](https://github.com/mwinters0/xpow/releases) or 8 | ```bash 9 | go install github.com/mwinters0/xpow@latest 10 | ``` 11 | 12 | ## example 13 | ab.txt: 14 | ``` 15 | a 16 | b 17 | ``` 18 | 19 | xy.txt: 20 | ``` 21 | x 22 | y 23 | ``` 24 | 25 | 78.txt: 26 | ``` 27 | 7 28 | 8 29 | ``` 30 | 31 | run: 32 | ```bash 33 | xpow ab.txt xy.txt 78.txt > out.txt 34 | ``` 35 | 36 | out.txt: 37 | ``` 38 | ax7 39 | ax8 40 | ay7 41 | ay8 42 | bx7 43 | bx8 44 | by7 45 | by8 46 | a7x 47 | a7y 48 | a8x 49 | a8y 50 | b7x 51 | b7y 52 | b8x 53 | b8y 54 | xa7 55 | xa8 56 | xb7 57 | xb8 58 | ya7 59 | ya8 60 | yb7 61 | yb8 62 | x7a 63 | x7b 64 | x8a 65 | x8b 66 | y7a 67 | y7b 68 | y8a 69 | y8b 70 | 7ax 71 | 7ay 72 | 7bx 73 | 7by 74 | 8ax 75 | 8ay 76 | 8bx 77 | 8by 78 | 7xa 79 | 7xb 80 | 7ya 81 | 7yb 82 | 8xa 83 | 8xb 84 | 8ya 85 | 8yb 86 | 87 | ``` -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Michael Winters 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "log" 7 | "os" 8 | "slices" 9 | ) 10 | 11 | func main() { 12 | if len(os.Args) < 2 { 13 | log.Fatalf("usage: %s ...", os.Args[0]) 14 | } 15 | if slices.Contains([]string{"-h", "--help", "-v", "--version", "version"}, os.Args[1]) { 16 | fmt.Println("v0.0.1") 17 | fmt.Println("https://github.com/mwinters0/xpow") 18 | os.Exit(0) 19 | } 20 | 21 | var words [][]string // list of list of words 22 | for i := 0; i < len(os.Args)-1; i++ { 23 | words = append(words, []string{}) 24 | inFile, err := os.Open(os.Args[i+1]) 25 | if err != nil { 26 | log.Fatalf("failed to open input file: %v", err) 27 | } 28 | scanner := bufio.NewScanner(inFile) 29 | for scanner.Scan() { 30 | words[i] = append(words[i], scanner.Text()) 31 | } 32 | if err = scanner.Err(); err != nil { 33 | log.Fatalf("error reading input file: %v", err) 34 | } 35 | inFile.Close() 36 | } 37 | 38 | var slotPermutations []int 39 | for i := 0; i < len(words); i++ { 40 | slotPermutations = append(slotPermutations, i) 41 | } 42 | permuteListIndices(slotPermutations, []int{}, 0, []int{}, func(ints []int) { 43 | permuteWords(words, ints, 0, "", func(s string) { 44 | fmt.Println(s) 45 | }) 46 | }) 47 | } 48 | 49 | func permuteListIndices(ints []int, used []int, curSlot int, soFar []int, emit func([]int)) { 50 | for i := 0; i < len(ints); i++ { 51 | if slices.Contains(used, ints[i]) { 52 | continue 53 | } 54 | if curSlot == len(ints)-1 { 55 | emit(append(soFar, ints[i])) 56 | } else { 57 | permuteListIndices(ints, append(used, ints[i]), curSlot+1, append(soFar, ints[i]), emit) 58 | } 59 | } 60 | } 61 | 62 | // number of lists = number of slots 63 | // each iteration, pull from words[listOrder[curSlot]] 64 | func permuteWords(words [][]string, listOrder []int, curSlot int, soFar string, emit func(string)) { 65 | for i := 0; i < len(words[listOrder[curSlot]]); i++ { 66 | thisIter := fmt.Sprintf("%s%s", soFar, words[listOrder[curSlot]][i]) 67 | if curSlot == len(words)-1 { 68 | // this is the last slot - emit result 69 | emit(thisIter) 70 | continue 71 | } 72 | // more slots to the right of me 73 | permuteWords(words, listOrder, curSlot+1, thisIter, emit) 74 | } 75 | } 76 | --------------------------------------------------------------------------------