Class: GDB::GDB

Inherits:
Object
  • Object
show all
Defined in:
lib/gdb/gdb.rb

Overview

For launching a gdb process.

Constant Summary collapse

SCRIPTS_PATH =

Absolute path to python scripts.

File.join(__dir__, 'scripts').freeze

Instance Method Summary collapse

Constructor Details

#initialize(arguments, gdb: 'gdb') ⇒ GDB

To launch a gdb instance.

Examples:

gdb = GDB::GDB.new('-q -nh bash')
gdb = GDB::GDB.new('arm.elf', gdb: 'gdb-multiarch')

27
28
29
30
31
32
33
# File 'lib/gdb/gdb.rb', line 27

def initialize(arguments, gdb: 'gdb')
  arguments = "--command=#{File.join(SCRIPTS_PATH, 'gdbinit.py')}" + ' ' + arguments # XXX
  @tube = spawn(gdb + ' ' + arguments)
  pre = @tube.readuntil('GDBRuby:')
  @prompt = @tube.readuntil("\n").strip
  @tube.unget(pre + @tube.readuntil(@prompt))
end

Instance Method Details

#alive?Boolean Also known as: running?

Is process running?

Actually judged by if #pid returns zero.


142
143
144
# File 'lib/gdb/gdb.rb', line 142

def alive?
  !pid.zero?
end

#break(point) ⇒ String Also known as: b

Set break point.

This method some magic, see parameters or examples.

Examples:

gdb = GDB::GDB.new('bash')
gdb.break('main')#=> "Breakpoint 1 at 0x41eed0"

gdb.break(0x41eed0)#=> "Note: breakpoint 1 also set at pc 0x41eed0.\r\nBreakpoint 2 at 0x41eed0"

74
75
76
77
78
79
# File 'lib/gdb/gdb.rb', line 74

def break(point)
  case point
  when Integer then execute("break *#{point}")
  when String then execute("break #{point}")
  end
end

#closevoid Also known as: quit

This method returns an undefined value.

Terminate the gdb process.


264
265
266
267
268
269
# File 'lib/gdb/gdb.rb', line 264

def close
  return if @tube.closed?
  @tube.close
  Process.wait(@gdb_pid)
  nil
end

#execute(cmd) ⇒ String Also known as: exec

Execute a command in gdb.

Examples:

gdb = GDB::GDB.new('bash')
gdb.execute('b main')#=> "Breakpoint 1 at 0x41eed0"

gdb.execute('run')
gdb.execute('print $rsi')#=> "$1 = 0x7fffffffdef8"

50
51
52
53
# File 'lib/gdb/gdb.rb', line 50

def execute(cmd)
  @tube.puts(cmd)
  @tube.readuntil(@prompt).strip
end

#interactvoid

This method returns an undefined value.

Enter gdb interactive mode. Gdb will be closed after interaction.


256
257
258
259
# File 'lib/gdb/gdb.rb', line 256

def interact
  $stdin.raw { @tube.interact(method(:output_hook)) }
  close
end

#pidInteger

Get the process's pid.

This method implemented by invoking python print(gdb.selected_inferior().pid).


153
154
155
# File 'lib/gdb/gdb.rb', line 153

def pid
  @pid = python_p('gdb.selected_inferior().pid').to_i
end

#python_p(cmd) ⇒ String

To simplify the frequency call of python print(xxx).


248
249
250
# File 'lib/gdb/gdb.rb', line 248

def python_p(cmd)
  execute("python print(#{cmd})")
end

#read_memory(addr, num_elements, options = {}) {|io| ... } ⇒ Object+ Also known as: readm

Read current process's memory.

Examples:

gdb = GDB::GDB.new('spec/binaries/amd64.elf')
gdb.break('main')
gdb.run
gdb.read_memory('amd64.elf', 4)#=> "\x7fELF"
# example of fetching argv
gdb = GDB::GDB.new('spec/binaries/amd64.elf')
gdb.break('main')
gdb.run('pusheen the cat')
gdb.read_memory(0x400000, 4)#=> "\x7fELF"

argc = gdb.register(:rdi)#=> 4

args = gdb.read_memory(gdb.register(:rsi), argc, as: :u64)
Array.new(3) do |i|
  gdb.read_memory(args[i + 1], 1) do |m|
    str = ''
    loop do
      c = m.read(1)
      break if c == "\x00"
      str << c
    end
    str
  end
end#=> ["pusheen", "the", "cat"]


# or, use our build-in types of gem +memory_io+.
gdb.read_memory(args[1], 3, as: :c_str)#=> ["pusheen", "the", "cat"]

Yield Parameters:

  • io (IO)

    The IO object that points to addr, read from it.

Yield Returns:

  • (Object)

    Whatever you read from io.


215
216
217
218
219
# File 'lib/gdb/gdb.rb', line 215

def read_memory(addr, num_elements, options = {}, &block)
  check_alive! # this would set @pid
  options[:as] = block if block_given?
  MemoryIO.attach(@pid).read(addr, num_elements, **options)
end

#register(reg_name) ⇒ Integer Also known as: reg

TODO:

Handle when reg_name is not a general-purpose register.

Get current value of register


115
116
117
118
# File 'lib/gdb/gdb.rb', line 115

def register(reg_name)
  check_alive!
  Integer(python_p("gdb.parse_and_eval('$#{reg_name}')").split.first)
end

#run(args = '') ⇒ String Also known as: r

Note:

If breakpoints are not set properly and cause gdb hangs, this method will hang, too.

Run process.

Examples:

gdb = GDB::GDB.new('bash')
puts gdb.run('-c "echo 111"')# Starting program: /bin/bash -c "echo 111"
# 111
# [Inferior 1 (process 3229) exited normally]
#=> nil

101
102
103
# File 'lib/gdb/gdb.rb', line 101

def run(args = '')
  execute('run ' + args)
end

#text_baseInteger Also known as: code_base

Note:

This will also set a variable $text in gdb.

Get the process's text base.


128
129
130
131
132
133
# File 'lib/gdb/gdb.rb', line 128

def text_base
  check_alive!
  base = Integer(execute('info proc stat').scan(/Start of text: (.*)/).flatten.first)
  execute("set $text = #{base}")
  base
end

#write_memory(addr, objects, options = {}, &block) ⇒ void Also known as: writem

This method returns an undefined value.

Write an object to process at specific address.


235
236
237
238
239
# File 'lib/gdb/gdb.rb', line 235

def write_memory(addr, objects, options = {}, &block)
  check_alive! # this would set @pid
  options[:as] = block if block_given?
  MemoryIO.attach(@pid).write(addr, objects, **options)
end