-
Notifications
You must be signed in to change notification settings - Fork 163
/
find-ppr.py
147 lines (127 loc) · 4.67 KB
/
find-ppr.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import pykd
import argparse
from enum import Enum, auto
def hex_byte(byte_str):
"""validate user input is a hex representation of an int between 0 and 255 inclusive"""
if byte_str == "??":
# windbg shows ?? when it can't access a memory region, but we shouldn't stop execution because of it
return byte_str
try:
val = int(byte_str, 16)
if 0 <= val <= 255:
return val
else:
raise ValueError
except ValueError:
raise argparse.ArgumentTypeError(
f"only *hex* bytes between 00 and ff are valid, found {byte_str}"
)
class Module:
# 00400000 00465000 diskpls (deferred)
def __init__(self, unparsed):
self.name = "unknown"
self.start = -1
self.end = -1
self.unparsed = unparsed.split()
self.parse()
def parse(self):
if len(self.unparsed) >= 3:
self.start = self.unparsed[0]
self.end = self.unparsed[1]
self.name = self.unparsed[2]
def __str__(self):
return f"{self.name}(start={self.start}, end={self.end})"
class PopR32(Enum):
eax = 0x58
ecx = auto()
edx = auto()
ebx = auto()
esp = auto()
esi = auto()
edi = auto()
def checkBadChars(bAddr, badChars):
for i in bAddr:
if i in badChars:
return "--"
return "OK"
def main(args):
modules = pykd.dbgCommand("lm")
totalGadgets = 0 # This tracks all the total number of usable gadgets
modGadgetCount = {} # This tracks the number of gadgets per module
for mod_line in modules.splitlines():
module = Module(mod_line)
if module.name.lower() not in [mod.lower() for mod in args.modules]:
continue
numGadgets = 0 # This is the number of gadgets found in this module
print(f"[+] searching {module.name} for pop r32; pop r32; ret")
print("[+] BADCHARS: ", end="")
for i in args.bad:
print("\\x{:02X}".format(i), end="")
print()
for pop1 in range(0x58, 0x60):
for pop2 in range(0x58, 0x60):
command = (
f"s-[1]b {module.start} {module.end} {hex(pop1)} {hex(pop2)} c3"
)
result = pykd.dbgCommand(command)
if result is None:
continue
for addr in result.splitlines():
try:
bAddr = int(addr, 16).to_bytes(4, "little")
bcChk = checkBadChars(bAddr, args.bad)
bAddrEsc = "" # This is the escaped string containing the little endian addr for shellcode output
for b in bAddr:
bAddrEsc += "\\x{:02X}".format(b)
if args.showbc and bcChk == "--":
print(
f"[{bcChk}] {module.name}::{addr}: pop {PopR32(pop1).name}; pop {PopR32(pop2).name}; ret ; {bAddrEsc}"
)
elif bcChk == "OK":
print(
f"[{bcChk}] {module.name}::{addr}: pop {PopR32(pop1).name}; pop {PopR32(pop2).name}; ret ; {bAddrEsc}"
)
numGadgets = numGadgets + 1
except ValueError:
# not a valid pop r32
pass
print(f"[+] {module.name}: Found {numGadgets} usable gadgets!")
modGadgetCount[module.name] = numGadgets # Add to the dict
totalGadgets = (
totalGadgets + numGadgets
) # Increment total number of gadgets found
print("\n---- STATS ----") # Print out all the stats
print(">> BADCHARS: ", end="")
for i in args.bad:
print("\\x{:02X}".format(i), end="")
print()
print(f">> Usable Gadgets Found: {totalGadgets}")
print(">> Module Gadget Counts")
for m, c in modGadgetCount.items():
print(" - {}: {} ".format(m, c))
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"-s",
"--showbc",
help="Show addresses with bad chars",
action="store_true"
)
parser.add_argument(
"-b",
"--bad",
help="space separated list of hex bytes that are already known bad (ex: -b 00 0a 0d)",
nargs="+",
type=hex_byte,
default=[],
)
parser.add_argument(
"-m",
"--modules",
help="module name(s) to search for pop pop ret (ex: find-ppr.py libspp diskpls libpal)",
required=True,
nargs="+",
)
args = parser.parse_args()
main(args)
print("Done!")