81 lines
2.3 KiB
Go
81 lines
2.3 KiB
Go
package winpwn
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"os/exec"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// NewROPExternal is the explicit-tool-path variant of NewROP, for an rp-win
|
|
// build kept somewhere other than RP_WIN_EXE/defaultRPWinTool, or a
|
|
// different rp++ fork entirely (same output format).
|
|
func NewROPExternal(binaryPath string, toolPath string) (*ROP, error) {
|
|
return newROP(binaryPath, toolPath)
|
|
}
|
|
|
|
func (r *ROP) findGadgetsExternal() error {
|
|
// -f: target file
|
|
// -r 5: max gadget length in instructions
|
|
// --unique: dedupe identical instruction sequences
|
|
// --allow-branches: also report gadgets terminated by an indirect
|
|
// jmp/call (register or memory operand) and jmp-$ self-loops, not just
|
|
// ret/ret-imm16 -- the JOP transit primitives needed when the target
|
|
// has no clean `pop reg ; ret` for a given register.
|
|
cmd := exec.Command(r.toolPath, "-f", r.binaryPath, "-r", "5", "--unique", "--allow-branches")
|
|
|
|
var out, stderr bytes.Buffer
|
|
cmd.Stdout = &out
|
|
cmd.Stderr = &stderr
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("failed to run gadget finder %q: %w\n%s", r.toolPath, err, stderr.String())
|
|
}
|
|
|
|
// Регулярное выражение для парсинга вывода rp++
|
|
// Пример строки: "0x140001234: pop rcx ; ret ; (1 found)"
|
|
re := regexp.MustCompile(`^(0x[0-9a-fA-F]+):\s*(.+?)\s*(?:;\s*\(\d+ found\))?$`)
|
|
|
|
scanner := bufio.NewScanner(&out)
|
|
for scanner.Scan() {
|
|
line := strings.TrimSpace(scanner.Text())
|
|
|
|
matches := re.FindStringSubmatch(line)
|
|
if len(matches) >= 3 {
|
|
addrStr := matches[1]
|
|
instructions := matches[2]
|
|
|
|
// Конвертация hex-строки в uint64
|
|
addr, err := strconv.ParseUint(strings.TrimPrefix(addrStr, "0x"), 16, 64)
|
|
if err == nil {
|
|
r.gadgets = append(r.gadgets, Gadget{
|
|
Address: addr,
|
|
Instructions: instructions,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Search ищет гаджет по подстроке (например, "pop rcx ; ret")
|
|
func (r *ROP) Search(instr string) ([]Gadget, error) {
|
|
var results []Gadget
|
|
searchStr := strings.ToLower(instr)
|
|
|
|
for _, g := range r.gadgets {
|
|
if strings.Contains(strings.ToLower(g.Instructions), searchStr) {
|
|
results = append(results, g)
|
|
}
|
|
}
|
|
|
|
if len(results) == 0 {
|
|
return nil, fmt.Errorf("gadget '%s' not found", instr)
|
|
}
|
|
return results, nil
|
|
}
|