Module: PWN::Plugins::Debugger
- Defined in:
- lib/pwn/plugins/debugger.rb
Overview
Structured GDB/MI driver with a handle registry that survives pwn_eval.
Class Method Summary collapse
- .attach(opts = {}) ⇒ Object
- .authors ⇒ Object
- .backtrace(opts = {}) ⇒ Object
- .break(opts = {}) ⇒ Object
- .checksec(opts = {}) ⇒ Object
- .close(opts = {}) ⇒ Object
- .continue(opts = {}) ⇒ Object
- .cyclic(opts = {}) ⇒ Object
- .cyclic_find(opts = {}) ⇒ Object
- .help ⇒ Object
- .launch(opts = {}) ⇒ Object
- .read_mem(opts = {}) ⇒ Object
- .regs(opts = {}) ⇒ Object
- .required_bins ⇒ Object
- .run(opts = {}) ⇒ Object
- .step(opts = {}) ⇒ Object
- .to_pwntools_offsets(opts = {}) ⇒ Object
- .write_mem(opts = {}) ⇒ Object
Class Method Details
.attach(opts = {}) ⇒ Object
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/pwn/plugins/debugger.rb', line 30 public_class_method def self.attach(opts = {}) if opts[:host] || opts[:port] host = (opts[:host] || '127.0.0.1').to_s port = (opts[:port] || 1234).to_i argv = ['gdb', '--interpreter=mi2', '--quiet', '--nx'] tube = PWN::Plugins::ProcessTube.spawn(cmd: argv, name: opts[:handle]) row = store_session(handle: tube[:id], tube: tube, gdbstub: "#{host}:#{port}") drain_gdb(handle: row[:handle]) parse_mi(raw: mi(handle: row[:handle], cmd: "-target-select remote #{host}:#{port}")) return row end pid = opts[:pid].to_i raise ArgumentError, 'pid is required' unless pid.positive? argv = ['gdb', '--interpreter=mi2', '--quiet', '--nx', '-p', pid.to_s] tube = PWN::Plugins::ProcessTube.spawn(cmd: argv, name: opts[:handle]) row = store_session(handle: tube[:id], tube: tube, pid: pid) drain_gdb(handle: row[:handle]) row end |
.authors ⇒ Object
117 118 119 |
# File 'lib/pwn/plugins/debugger.rb', line 117 public_class_method def self. "AUTHOR(S):\n 0day Inc. <[email protected]>\n" end |
.backtrace(opts = {}) ⇒ Object
90 91 92 |
# File 'lib/pwn/plugins/debugger.rb', line 90 public_class_method def self.backtrace(opts = {}) parse_mi(raw: mi(opts.merge(cmd: '-stack-list-frames'))) end |
.break(opts = {}) ⇒ Object
55 56 57 58 59 60 |
# File 'lib/pwn/plugins/debugger.rb', line 55 public_class_method def self.break(opts = {}) loc = (opts[:addr_or_sym] || opts[:location] || opts[:addr] || opts[:symbol]).to_s raise ArgumentError, 'addr_or_sym is required' if loc.empty? parse_mi(raw: mi(opts.merge(cmd: "-break-insert #{loc}"))) end |
.checksec(opts = {}) ⇒ Object
94 95 96 |
# File 'lib/pwn/plugins/debugger.rb', line 94 public_class_method def self.checksec(opts = {}) PWN::Plugins::GDB.mitigations(binary: opts[:bin] || opts[:binary] || session!(opts)[:bin]) end |
.close(opts = {}) ⇒ Object
110 111 112 113 114 115 |
# File 'lib/pwn/plugins/debugger.rb', line 110 public_class_method def self.close(opts = {}) handle = (opts[:handle] || opts[:id]).to_s sess = @sessions.delete(handle) PWN::Plugins::ProcessTube.close(id: handle) if sess { closed: handle } end |
.continue(opts = {}) ⇒ Object
62 63 64 |
# File 'lib/pwn/plugins/debugger.rb', line 62 public_class_method def self.continue(opts = {}) parse_mi(raw: mi(opts.merge(cmd: '-exec-continue'))) end |
.cyclic(opts = {}) ⇒ Object
98 99 100 |
# File 'lib/pwn/plugins/debugger.rb', line 98 public_class_method def self.cyclic(opts = {}) PWN::Plugins::ExploitDev.cyclic(length: opts[:length] || opts[:n] || 200) end |
.cyclic_find(opts = {}) ⇒ Object
102 103 104 |
# File 'lib/pwn/plugins/debugger.rb', line 102 public_class_method def self.cyclic_find(opts = {}) PWN::Plugins::ExploitDev.cyclic_find(value: opts[:value] || opts[:pattern], length: opts[:length] || 8_192) end |
.help ⇒ Object
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 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/pwn/plugins/debugger.rb', line 121 public_class_method def self.help puts "USAGE: # List host binaries this module expects to be installed. #{self}.required_bins # Launch a binary under gdb MI and keep the handle in-process. #{self}.launch( bin: 'required - filesystem path of the binary to debug', binary: 'optional - alias for bin', args: 'optional - Array of argv strings after the binary', handle: 'optional - ProcessTube name for the session', run: 'optional - true issues -exec-run after gdb starts' ) # Attach gdb MI to an existing pid or gdbstub. #{self}.attach( pid: 'optional - integer process id to attach', host: 'optional - gdbstub host for remote attach', port: 'optional - gdbstub TCP port', handle: 'optional - ProcessTube name for the session' ) # Run the inferior (-exec-run). #{self}.run( handle: 'required - session handle from launch or attach' ) # Insert a breakpoint at a symbol or address. #{self}.break( handle: 'required - session handle from launch or attach', addr_or_sym: 'required - symbol or address', location: 'optional - alias for addr_or_sym', addr: 'optional - alias for addr_or_sym', symbol: 'optional - alias for addr_or_sym' ) # Continue execution until the next stop. #{self}.continue( handle: 'required - session handle from launch or attach' ) # Step one instruction or source line. #{self}.step( handle: 'required - session handle from launch or attach', into: 'optional - true to step into rather than next' ) # Read memory bytes at an address. #{self}.read_mem( handle: 'required - session handle from launch or attach', addr: 'required - address to read', address: 'optional - alias for addr', len: 'optional - byte count (defaults to 64)', length: 'optional - alias for len' ) # Write raw bytes to inferior memory. #{self}.write_mem( handle: 'required - session handle from launch or attach', addr: 'required - address to write', address: 'optional - alias for addr', data: 'required - String or byte Array to write', bytes: 'optional - alias for data' ) # Return register values as a parsed MI hash. #{self}.regs( handle: 'required - session handle from launch or attach' ) # Return a parsed backtrace hash. #{self}.backtrace( handle: 'required - session handle from launch or attach' ) # Return binary mitigations via GDB.mitigations. #{self}.checksec( handle: 'optional - session handle whose binary should be probed', bin: 'optional - filesystem path of the binary', binary: 'optional - alias for bin' ) # Generate a cyclic de Bruijn pattern. #{self}.cyclic( length: 'optional - pattern length in bytes', n: 'optional - alias for length' ) # Find the offset of a packed register value in a cyclic pattern. #{self}.cyclic_find( value: 'required - leaked register value or substring', pattern: 'optional - alias for value', length: 'optional - haystack length (defaults to 8192)' ) # Return both a cyclic pattern and the recovered offset. #{self}.to_pwntools_offsets( value: 'optional - leaked register value for cyclic_find', length: 'optional - pattern length in bytes', n: 'optional - alias for length' ) # Close a debugger session handle. #{self}.close( handle: 'required - session handle from launch or attach', id: 'optional - alias for handle' ) # Print the AUTHOR(S) string for this module. #{self}.authors " constants.sort end |
.launch(opts = {}) ⇒ Object
15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/pwn/plugins/debugger.rb', line 15 public_class_method def self.launch(opts = {}) bin = (opts[:bin] || opts[:binary]).to_s raise ArgumentError, 'bin is required' if bin.empty? args = Array(opts[:args]).map(&:to_s) argv = ['gdb', '--interpreter=mi2', '--quiet', '--nx', '--args', bin, *args] tube = PWN::Plugins::ProcessTube.spawn(cmd: argv, name: opts[:handle]) row = store_session(handle: tube[:id], tube: tube, bin: bin) drain_gdb(handle: row[:handle]) mi(handle: row[:handle], cmd: '-gdb-set confirm off') mi(handle: row[:handle], cmd: '-gdb-set pagination off') mi(handle: row[:handle], cmd: '-exec-run') if opts[:run] == true row end |
.read_mem(opts = {}) ⇒ Object
70 71 72 73 74 75 |
# File 'lib/pwn/plugins/debugger.rb', line 70 public_class_method def self.read_mem(opts = {}) addr = (opts[:addr] || opts[:address]).to_s raise ArgumentError, 'addr is required' if addr.empty? parse_mi(raw: mi(opts.merge(cmd: "-data-read-memory-bytes #{addr} #{(opts[:len] || opts[:length] || 64).to_i}"))) end |
.regs(opts = {}) ⇒ Object
86 87 88 |
# File 'lib/pwn/plugins/debugger.rb', line 86 public_class_method def self.regs(opts = {}) parse_mi(raw: mi(opts.merge(cmd: '-data-list-register-values x'))) end |
.required_bins ⇒ Object
11 12 13 |
# File 'lib/pwn/plugins/debugger.rb', line 11 public_class_method def self.required_bins %w[gdb] end |
.run(opts = {}) ⇒ Object
51 52 53 |
# File 'lib/pwn/plugins/debugger.rb', line 51 public_class_method def self.run(opts = {}) parse_mi(raw: mi(opts.merge(cmd: '-exec-run'))) end |
.step(opts = {}) ⇒ Object
66 67 68 |
# File 'lib/pwn/plugins/debugger.rb', line 66 public_class_method def self.step(opts = {}) parse_mi(raw: mi(opts.merge(cmd: (opts[:into] ? '-exec-step' : '-exec-next')))) end |
.to_pwntools_offsets(opts = {}) ⇒ Object
106 107 108 |
# File 'lib/pwn/plugins/debugger.rb', line 106 public_class_method def self.to_pwntools_offsets(opts = {}) { cyclic: cyclic(opts), offset: cyclic_find(opts) } end |
.write_mem(opts = {}) ⇒ Object
77 78 79 80 81 82 83 84 |
# File 'lib/pwn/plugins/debugger.rb', line 77 public_class_method def self.write_mem(opts = {}) addr = (opts[:addr] || opts[:address]).to_s data = opts[:data] || opts[:bytes] raise ArgumentError, 'addr and data are required' if addr.empty? || data.nil? hex = data.is_a?(String) ? data.unpack1('H*') : Array(data).pack('C*').unpack1('H*') parse_mi(raw: mi(opts.merge(cmd: "-data-write-memory-bytes #{addr} #{hex}"))) end |