Module: OneGadget::Helper
- Defined in:
- lib/one_gadget/helper.rb
Overview
Define some helpful methods here.
Constant Summary collapse
- BUILD_ID_FORMAT =
Format of build-id, 40 hex numbers.
/[0-9a-f]{40}/.freeze
- COLOR_CODE =
Color codes for pretty print
{ esc_m: "\e[0m", normal_s: "\e[38;5;203m", # red integer: "\e[38;5;189m", # light purple reg: "\e[38;5;82m", # light green warn: "\e[38;5;230m", # light yellow error: "\e[38;5;196m" # heavy red }.freeze
Class Method Summary collapse
-
.abspath(path) ⇒ String
Get absolute path from relative path.
-
.arch_specific_objdump(arch) ⇒ String
Returns the binary name of objdump.
-
.architecture(file) ⇒ Symbol
Fetch the ELF architecture of
file
. -
.build_id_of(path) ⇒ String
Get the Build ID of target ELF.
-
.color_enabled? ⇒ Boolean
Is colorize output enabled?.
-
.color_off! ⇒ void
Disable colorize.
-
.colored_hex(val) ⇒ String
Returns the hexified and colorized integer.
-
.colorize(str, sev: :normal_s) ⇒ String
Wrap string with color codes for pretty inspect.
-
.comments_of_file(file) ⇒ Array<String>
Fetch lines start with ‘#’.
-
.download_build(file) ⇒ Tempfile
Download the latest version of
file
inlib/one_gadget/builds/
from remote repo. -
.find_objdump(arch) ⇒ String?
Find objdump that supports architecture
arch
. -
.function_offsets(file, functions) ⇒ Hash{String => Integer}
Returns a dictionary that maps functions to their offsets.
-
.got_functions(file) ⇒ Array<String>
Returns the names of functions from the file’s global offset table.
-
.hex(val, psign: false) ⇒ String
Present number in hex format.
-
.integer?(str) ⇒ Boolean
Checks if a string can be converted into an integer.
-
.latest_tag ⇒ String
Fetch the latest release version’s tag name.
-
.objdump_arch(arch) ⇒ String
Converts to the architecture name shown in objdump’s
--help
command. -
.objdump_arch_supported?(bin, arch) ⇒ Boolean
Checks if the given objdump supports certain architecture.
-
.remote_builds ⇒ Array<String>
Get the latest builds list from repo.
-
.url_of_file(filename) ⇒ String
Get the url which can fetch
filename
from remote repo. -
.url_request(url) ⇒ String
Get request.
-
.valid_elf_file?(path) ⇒ Boolean
Checks if the file of given path is a valid ELF file.
-
.verify_build_id!(build_id) ⇒ void
Checks if
build_id
is a valid SHA1 hex format. -
.verify_elf_file!(path) ⇒ void
Checks if the file of given path is a valid ELF file.
-
.which(cmd) ⇒ String?
Cross-platform way of finding an executable in $PATH.
Class Method Details
.abspath(path) ⇒ String
Get absolute path from relative path. Support symlink.
47 48 49 |
# File 'lib/one_gadget/helper.rb', line 47 def abspath(path) Pathname.new(File.(path)).realpath.to_s end |
.arch_specific_objdump(arch) ⇒ String
Returns the binary name of objdump.
317 318 319 320 321 322 323 |
# File 'lib/one_gadget/helper.rb', line 317 def arch_specific_objdump(arch) { aarch64: 'aarch64-linux-gnu-objdump', amd64: 'x86_64-linux-gnu-objdump', i386: 'i686-linux-gnu-objdump' }[arch] end |
.architecture(file) ⇒ Symbol
Fetch the ELF architecture of file
.
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/one_gadget/helper.rb', line 197 def architecture(file) return :invalid unless File.exist?(file) f = File.open(file) str = ELFTools::ELFFile.new(f).machine { 'Advanced Micro Devices X86-64' => :amd64, 'Intel 80386' => :i386, 'ARM' => :arm, 'AArch64' => :aarch64, 'MIPS R3000' => :mips }[str] || :unknown rescue ELFTools::ELFError # not a valid ELF :invalid ensure f&.close end |
.build_id_of(path) ⇒ String
Get the Build ID of target ELF.
88 89 90 |
# File 'lib/one_gadget/helper.rb', line 88 def build_id_of(path) File.open(path) { |f| ELFTools::ELFFile.new(f).build_id } end |
.color_enabled? ⇒ Boolean
Is colorize output enabled?
101 102 103 104 105 106 |
# File 'lib/one_gadget/helper.rb', line 101 def color_enabled? # if not set, use tty to check return $stdout.tty? unless instance_variable_defined?(:@disable_color) !@disable_color end |
.color_off! ⇒ void
This method returns an undefined value.
Disable colorize.
94 95 96 |
# File 'lib/one_gadget/helper.rb', line 94 def color_off! @disable_color = true end |
.colored_hex(val) ⇒ String
Returns the hexified and colorized integer.
133 134 135 |
# File 'lib/one_gadget/helper.rb', line 133 def colored_hex(val) colorize(hex(val), sev: :integer) end |
.colorize(str, sev: :normal_s) ⇒ String
Wrap string with color codes for pretty inspect.
122 123 124 125 126 127 128 |
# File 'lib/one_gadget/helper.rb', line 122 def colorize(str, sev: :normal_s) return str unless color_enabled? cc = COLOR_CODE color = cc.key?(sev) ? cc[sev] : '' "#{color}#{str.sub(cc[:esc_m], color)}#{cc[:esc_m]}" end |
.comments_of_file(file) ⇒ Array<String>
Fetch lines start with ‘#’.
37 38 39 |
# File 'lib/one_gadget/helper.rb', line 37 def comments_of_file(file) File.readlines(file).map { |s| s[2..-1].rstrip if s.start_with?('# ') }.compact end |
.download_build(file) ⇒ Tempfile
Download the latest version of file
in lib/one_gadget/builds/
from remote repo.
156 157 158 159 160 |
# File 'lib/one_gadget/helper.rb', line 156 def download_build(file) temp = Tempfile.new(['gadgets', "#{file}.rb"]) temp.write(url_request(url_of_file(File.join('lib', 'one_gadget', 'builds', "#{file}.rb")))) temp.tap(&:close) end |
.find_objdump(arch) ⇒ String?
Find objdump that supports architecture arch
.
278 279 280 281 282 283 |
# File 'lib/one_gadget/helper.rb', line 278 def find_objdump(arch) [ which('objdump'), which(arch_specific_objdump(arch)) ].find { |bin| objdump_arch_supported?(bin, arch) } end |
.function_offsets(file, functions) ⇒ Hash{String => Integer}
Returns a dictionary that maps functions to their offsets.
338 339 340 341 342 343 344 345 346 347 348 349 |
# File 'lib/one_gadget/helper.rb', line 338 def function_offsets(file, functions) arch = architecture(file) objdump_bin = find_objdump(arch) objdump_cmd = ::Shellwords.join([objdump_bin, '-T', file]) functions.map! { |f| "\\b#{f}\\b" } ret = {} `#{objdump_cmd} | grep -iP '(#{functions.join('|')})'`.lines.map(&:chomp).each do |line| tokens = line.split ret[tokens.last] = tokens.first.to_i(16) end ret end |
.got_functions(file) ⇒ Array<String>
Returns the names of functions from the file’s global offset table.
328 329 330 331 332 |
# File 'lib/one_gadget/helper.rb', line 328 def got_functions(file) arch = architecture(file) objdump_bin = find_objdump(arch) `#{::Shellwords.join([objdump_bin, '-T', file])} | grep -iPo 'GLIBC_.+?\\s+\\K.*'`.split end |
.hex(val, psign: false) ⇒ String
Present number in hex format.
228 229 230 231 232 |
# File 'lib/one_gadget/helper.rb', line 228 def hex(val, psign: false) return format("#{psign ? '+' : ''}0x%x", val) if val >= 0 format('-0x%x', -val) end |
.integer?(str) ⇒ Boolean
Checks if a string can be converted into an integer.
246 247 248 249 250 |
# File 'lib/one_gadget/helper.rb', line 246 def integer?(str) true if Integer(str) rescue ArgumentError, TypeError false end |
.latest_tag ⇒ String
Fetch the latest release version’s tag name.
139 140 141 142 |
# File 'lib/one_gadget/helper.rb', line 139 def latest_tag releases_url = 'https://github.com/david942j/one_gadget/releases/latest' @latest_tag ||= url_request(releases_url).split('/').last end |
.objdump_arch(arch) ⇒ String
Converts to the architecture name shown in objdump’s --help
command.
307 308 309 310 311 312 |
# File 'lib/one_gadget/helper.rb', line 307 def objdump_arch(arch) case arch when :amd64 then 'i386:x86-64' else arch.to_s end end |
.objdump_arch_supported?(bin, arch) ⇒ Boolean
Checks if the given objdump supports certain architecture.
292 293 294 295 296 297 |
# File 'lib/one_gadget/helper.rb', line 292 def objdump_arch_supported?(bin, arch) return false if bin.nil? arch = objdump_arch(arch) `#{::Shellwords.join([bin, '--help'])}`.lines.any? { |c| c.split.include?(arch) } end |
.remote_builds ⇒ Array<String>
Get the latest builds list from repo.
164 165 166 |
# File 'lib/one_gadget/helper.rb', line 164 def remote_builds @remote_builds ||= url_request(url_of_file('builds_list')).lines.map(&:strip) end |
.url_of_file(filename) ⇒ String
Get the url which can fetch filename
from remote repo.
147 148 149 150 |
# File 'lib/one_gadget/helper.rb', line 147 def url_of_file(filename) raw_file_url = 'https://raw.githubusercontent.com/david942j/one_gadget/@tag/@file' raw_file_url.sub('@tag', latest_tag).sub('@file', filename) end |
.url_request(url) ⇒ String
Get request.
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/one_gadget/helper.rb', line 173 def url_request(url) uri = URI.parse(url) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true http.verify_mode = ::OpenSSL::SSL::VERIFY_NONE request = Net::HTTP::Get.new(uri.request_uri) response = http.request(request) raise ArgumentError, "Fail to get response of #{url}" unless %w[200 302].include?(response.code) response.code == '302' ? response['location'] : response.body rescue NoMethodError, SocketError, ArgumentError => e OneGadget::Logger.error(e.) nil end |
.valid_elf_file?(path) ⇒ Boolean
Checks if the file of given path is a valid ELF file.
60 61 62 63 64 65 66 67 |
# File 'lib/one_gadget/helper.rb', line 60 def valid_elf_file?(path) # A light-weight way to check if is a valid ELF file # Checks at least one phdr should present. File.open(path) { |f| ELFTools::ELFFile.new(f).each_segments.first } true rescue ELFTools::ELFError false end |
.verify_build_id!(build_id) ⇒ void
This method returns an undefined value.
Checks if build_id
is a valid SHA1 hex format.
26 27 28 29 30 |
# File 'lib/one_gadget/helper.rb', line 26 def verify_build_id!(build_id) return if build_id =~ /\A#{OneGadget::Helper::BUILD_ID_FORMAT}\Z/ raise OneGadget::Error::ArgumentError, format('invalid BuildID format: %p', build_id) end |
.verify_elf_file!(path) ⇒ void
This method returns an undefined value.
Checks if the file of given path is a valid ELF file.
An error message will be shown if given path is not a valid ELF.
76 77 78 79 80 |
# File 'lib/one_gadget/helper.rb', line 76 def verify_elf_file!(path) return if valid_elf_file?(path) raise Error::ArgumentError, 'Not an ELF file, expected glibc as input' end |
.which(cmd) ⇒ String?
Cross-platform way of finding an executable in $PATH.
259 260 261 262 263 264 265 266 267 268 |
# File 'lib/one_gadget/helper.rb', line 259 def which(cmd) exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : [''] ENV['PATH'].split(File::PATH_SEPARATOR).each do |path| exts.each do |ext| exe = File.join(path, "#{cmd}#{ext}") return exe if File.executable?(exe) && !File.directory?(exe) end end nil end |