Refactor (#66)

* Update README.md
* Update docs
* Add a test of fetching remote build to ensure full coverage
* Refactor and clean code
This commit is contained in:
david942j 2019-03-06 13:04:10 +08:00 committed by GitHub
parent dbcd9a9b07
commit 7c8e6a727d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 212 additions and 180 deletions

124
README.md
View File

@ -76,95 +76,97 @@ $ one_gadget -b 60131540dadc6796cab33388349e6e4e68692053
# [rsp+0x70] == NULL
$ one_gadget /lib32/libc.so.6
# 0x3a7cc execve("/bin/sh", esp+0x28, environ)
# constraints:
# esi is the GOT address of libc
# [esp+0x28] == NULL
#
# 0x3a7ce execve("/bin/sh", esp+0x2c, environ)
# constraints:
# esi is the GOT address of libc
# [esp+0x2c] == NULL
#
# 0x3a7d2 execve("/bin/sh", esp+0x30, environ)
# constraints:
# esi is the GOT address of libc
# [esp+0x30] == NULL
#
# 0x3a7d9 execve("/bin/sh", esp+0x34, environ)
# 0x3cbea execve("/bin/sh", esp+0x34, environ)
# constraints:
# esi is the GOT address of libc
# [esp+0x34] == NULL
#
# 0x5f875 execl("/bin/sh", eax)
# 0x3cbec execve("/bin/sh", esp+0x38, environ)
# constraints:
# esi is the GOT address of libc
# [esp+0x38] == NULL
#
# 0x3cbf0 execve("/bin/sh", esp+0x3c, environ)
# constraints:
# esi is the GOT address of libc
# [esp+0x3c] == NULL
#
# 0x3cbf7 execve("/bin/sh", esp+0x40, environ)
# constraints:
# esi is the GOT address of libc
# [esp+0x40] == NULL
#
# 0x6729f execl("/bin/sh", eax)
# constraints:
# esi is the GOT address of libc
# eax == NULL
#
# 0x5f876 execl("/bin/sh", [esp])
# 0x672a0 execl("/bin/sh", [esp])
# constraints:
# esi is the GOT address of libc
# [esp] == NULL
#
# 0x13573e execl("/bin/sh", eax)
# constraints:
# ebx is the GOT address of libc
# eax == NULL
#
# 0x13573f execl("/bin/sh", [esp])
# constraints:
# ebx is the GOT address of libc
# [esp] == NULL
$ one_gadget /lib/x86_64-linux-gnu/libc.so.6
# 0x45526 execve("/bin/sh", rsp+0x30, environ)
# 0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
# constraints:
# rax == NULL
# rcx == NULL
#
# 0x4557a execve("/bin/sh", rsp+0x30, environ)
# constraints:
# [rsp+0x30] == NULL
#
# 0xf1651 execve("/bin/sh", rsp+0x40, environ)
# 0x4f322 execve("/bin/sh", rsp+0x40, environ)
# constraints:
# [rsp+0x40] == NULL
#
# 0xf24cb execve("/bin/sh", rsp+0x60, environ)
# 0x10a38c execve("/bin/sh", rsp+0x70, environ)
# constraints:
# [rsp+0x60] == NULL
# [rsp+0x70] == NULL
# show all gadgets found
$ one_gadget /lib/x86_64-linux-gnu/libc.so.6 --level 1
# 0x45526 execve("/bin/sh", rsp+0x30, environ)
# 0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
# constraints:
# rax == NULL
# rcx == NULL
#
# 0x4557a execve("/bin/sh", rsp+0x30, environ)
# constraints:
# [rsp+0x30] == NULL
#
# 0xcde41 execve("/bin/sh", r15, r13)
# constraints:
# [r15] == NULL || r15 == NULL
# [r13] == NULL || r13 == NULL
#
# 0xce0e1 execve("/bin/sh", [rbp-0x78], [rbp-0x50])
# constraints:
# [[rbp-0x78]] == NULL || [rbp-0x78] == NULL
# [[rbp-0x50]] == NULL || [rbp-0x50] == NULL
#
# 0xce0e5 execve("/bin/sh", r9, [rbp-0x50])
# constraints:
# [r9] == NULL || r9 == NULL
# [[rbp-0x50]] == NULL || [rbp-0x50] == NULL
#
# 0xce0e9 execve("/bin/sh", r9, rdx)
# constraints:
# [r9] == NULL || r9 == NULL
# [rdx] == NULL || rdx == NULL
#
# 0xf1651 execve("/bin/sh", rsp+0x40, environ)
# 0x4f322 execve("/bin/sh", rsp+0x40, environ)
# constraints:
# [rsp+0x40] == NULL
#
# 0xf165d execve("/bin/sh", rsi, [rax])
# 0xe569f execve("/bin/sh", r14, r12)
# constraints:
# [r14] == NULL || r14 == NULL
# [r12] == NULL || r12 == NULL
#
# 0xe5858 execve("/bin/sh", [rbp-0x88], [rbp-0x70])
# constraints:
# [[rbp-0x88]] == NULL || [rbp-0x88] == NULL
# [[rbp-0x70]] == NULL || [rbp-0x70] == NULL
#
# 0xe585f execve("/bin/sh", r10, [rbp-0x70])
# constraints:
# [r10] == NULL || r10 == NULL
# [[rbp-0x70]] == NULL || [rbp-0x70] == NULL
#
# 0xe5863 execve("/bin/sh", r10, rdx)
# constraints:
# [r10] == NULL || r10 == NULL
# [rdx] == NULL || rdx == NULL
#
# 0x10a38c execve("/bin/sh", rsp+0x70, environ)
# constraints:
# [rsp+0x70] == NULL
#
# 0x10a398 execve("/bin/sh", rsi, [rax])
# constraints:
# [rsi] == NULL || rsi == NULL
# [[rax]] == NULL || [rax] == NULL
#
# 0xf24cb execve("/bin/sh", rsp+0x60, environ)
# constraints:
# [rsp+0x60] == NULL
```
@ -182,11 +184,11 @@ $ one_gadget ./spec/data/libc-2.19.so -s 'echo "offset ->"'
```ruby
require 'one_gadget'
OneGadget.gadgets(file: '/lib/x86_64-linux-gnu/libc.so.6')
#=> [283942, 284026, 988753, 992459]
#=> [324293, 324386, 1090444]
# or in shorter way
one_gadget('/lib/x86_64-linux-gnu/libc.so.6', level: 1)
#=> [283942, 284026, 843329, 844001, 844005, 844009, 988753, 988765, 992459]
#=> [324293, 324386, 939679, 940120, 940127, 940131, 1090444, 1090456]
# from build id
one_gadget('60131540dadc6796cab33388349e6e4e68692053')

View File

@ -1,5 +1,5 @@
module OneGadget
# define the abi of different architecture.
# Defines the abi of different architectures.
module ABI
# Define class methods here.
module ClassMethods

View File

@ -1,5 +1,5 @@
require 'one_gadget/emulators/x86'
require 'one_gadget/abi'
require 'one_gadget/emulators/x86'
module OneGadget
module Emulators

View File

@ -1,5 +1,5 @@
require 'one_gadget/emulators/x86'
require 'one_gadget/abi'
require 'one_gadget/emulators/x86'
module OneGadget
module Emulators

View File

@ -88,69 +88,69 @@ module OneGadget
registers.include?(reg)
end
def inst_mov(tar, src)
def inst_mov(dst, src)
src = OneGadget::Emulators::Lambda.parse(src, predefined: registers)
if register?(tar)
registers[tar] = src
if register?(dst)
registers[dst] = src
else
# Just ignore strange case...
return unless tar.include?(sp)
return unless dst.include?(sp)
tar = OneGadget::Emulators::Lambda.parse(tar, predefined: registers)
return if tar.deref_count != 1 # should not happen
dst = OneGadget::Emulators::Lambda.parse(dst, predefined: registers)
return if dst.deref_count != 1 # should not happen
tar.ref!
stack[tar.evaluate(eval_dict)] = src
dst.ref!
stack[dst.evaluate(eval_dict)] = src
end
end
# This instruction moves 128bits.
def inst_movaps(tar, src)
def inst_movaps(dst, src)
# XXX: here we only support `movaps [sp+*], xmm*`
# TODO: This need an extra constraint: sp & 0xf == 0
src, tar = check_xmm_sp(src, tar) { raise_unsupported('movaps', tar, src) }
off = tar.evaluate(eval_dict)
src, dst = check_xmm_sp(src, dst) { raise_unsupported('movaps', dst, src) }
off = dst.evaluate(eval_dict)
(128 / self.class.bits).times do |i|
stack[off + i * size_t] = src[i]
end
end
# Mov *src to tar[:64]
def inst_movq(tar, src)
# Mov *src to dst[:64]
def inst_movq(dst, src)
# XXX: here we only support `movq xmm*, [sp+*]`
tar, src = check_xmm_sp(tar, src) { raise_unsupported('movq', tar, src) }
dst, src = check_xmm_sp(dst, src) { raise_unsupported('movq', dst, src) }
off = src.evaluate(eval_dict)
(64 / self.class.bits).times do |i|
tar[i] = stack[off + i * size_t]
dst[i] = stack[off + i * size_t]
end
end
# Move *src to tar[64:128]
def inst_movhps(tar, src)
# Move *src to dst[64:128]
def inst_movhps(dst, src)
# XXX: here we only support `movhps xmm*, [sp+*]`
tar, src = check_xmm_sp(tar, src) { raise_unsupported('movhps', tar, src) }
dst, src = check_xmm_sp(dst, src) { raise_unsupported('movhps', dst, src) }
off = src.evaluate(eval_dict)
(64 / self.class.bits).times do |i|
tar[i + 64 / self.class.bits] = stack[off + i * size_t]
dst[i + 64 / self.class.bits] = stack[off + i * size_t]
end
end
# check if (tar, src) in form (xmm*, [sp+*])
def check_xmm_sp(tar, src)
return yield unless tar.start_with?('xmm') && register?(tar) && src.include?(sp)
# check if (dst, src) in form (xmm*, [sp+*])
def check_xmm_sp(dst, src)
return yield unless dst.start_with?('xmm') && register?(dst) && src.include?(sp)
tar_lm = OneGadget::Emulators::Lambda.parse(tar, predefined: registers)
dst_lm = OneGadget::Emulators::Lambda.parse(dst, predefined: registers)
src_lm = OneGadget::Emulators::Lambda.parse(src, predefined: registers)
return yield if src_lm.deref_count != 1
src_lm.ref!
[tar_lm, src_lm]
[dst_lm, src_lm]
end
def inst_lea(tar, src)
def inst_lea(dst, src)
src = OneGadget::Emulators::Lambda.parse(src, predefined: registers)
src.ref!
registers[tar] = src
registers[dst] = src
end
def inst_push(val)
@ -170,16 +170,16 @@ module OneGadget
registers[dst] = 0
end
def inst_add(tar, src)
def inst_add(dst, src)
src = OneGadget::Emulators::Lambda.parse(src, predefined: registers)
registers[tar] += src
registers[dst] += src
end
def inst_sub(tar, src)
def inst_sub(dst, src)
src = OneGadget::Emulators::Lambda.parse(src, predefined: registers)
raise Error::ArgumentError, "Can't handle -= of type #{src.class}" unless src.is_a?(Integer)
registers[tar] -= src
registers[dst] -= src
end
# yap, nop

View File

@ -15,7 +15,7 @@ module OneGadget
module_function
# Verify if `build_id` is a valid SHA1 hex format.
# Checks if +build_id+ is a valid SHA1 hex format.
# @param [String] build_id
# BuildID.
# @raise [Error::ArgumentError]
@ -40,7 +40,7 @@ module OneGadget
# @param [String] path Relative path.
# @return [String] Absolute path, with symlink resolved.
# @example
# abspath('/lib/x86_64-linux-gnu/libc.so.6')
# Helper.abspath('/lib/x86_64-linux-gnu/libc.so.6')
# #=> '/lib/x86_64-linux-gnu/libc-2.23.so'
def abspath(path)
Pathname.new(File.expand_path(path)).realpath.to_s
@ -51,10 +51,10 @@ module OneGadget
# @param [String] path Path to target file.
# @return [Boolean] If the file is an ELF or not.
# @example
# valid_elf_file?('/etc/passwd')
# => false
# valid_elf_file?('/lib64/ld-linux-x86-64.so.2')
# => true
# Helper.valid_elf_file?('/etc/passwd')
# #=> false
# Helper.valid_elf_file?('/lib64/ld-linux-x86-64.so.2')
# #=> true
def valid_elf_file?(path)
# A light-weight way to check if is a valid ELF file
# Checks at least one phdr should present.
@ -64,7 +64,8 @@ module OneGadget
false
end
# Checks if the file of given path is a valid ELF file
# 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.
#
# @param [String] path Path to target file.
@ -80,7 +81,7 @@ module OneGadget
# @param [String] path Absolute file path.
# @return [String] Target build id.
# @example
# build_id_of('/lib/x86_64-linux-gnu/libc-2.23.so')
# Helper.build_id_of('/lib/x86_64-linux-gnu/libc-2.23.so')
# #=> '60131540dadc6796cab33388349e6e4e68692053'
def build_id_of(path)
File.open(path) { |f| ELFTools::ELFFile.new(f).build_id }
@ -125,7 +126,7 @@ module OneGadget
end
# Fetch the latest release version's tag name.
# @return [String] The tag name, in form +vx.x.x+.
# @return [String] The tag name, in form +vX.X.X+.
def latest_tag
releases_url = 'https://github.com/david942j/one_gadget/releases/latest'
@latest_tag ||= url_request(releases_url).split('/').last
@ -145,7 +146,6 @@ module OneGadget
# @return [Tempfile] The temp file be created.
def download_build(file)
temp = Tempfile.new(['gadgets', file + '.rb'])
url_request(url_of_file(File.join('lib', 'one_gadget', 'builds', file + '.rb')))
temp.write(url_request(url_of_file(File.join('lib', 'one_gadget', 'builds', file + '.rb'))))
temp.tap(&:close)
end
@ -160,7 +160,7 @@ module OneGadget
# @param [String] url The url.
# @return [String]
# The request response body.
# If the response is '302 Found', return the location in header.
# If the response is +302 Found+, returns the location in header.
def url_request(url)
uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)
@ -178,10 +178,13 @@ module OneGadget
nil
end
# Fetch the file archiecture of +file+.
# Fetch the ELF archiecture of +file+.
# @param [String] file The target ELF filename.
# @return [Symbol]
# Only supports architecture amd64 and i386 now.
# Currently supports amd64, i386, arm, aarch64, and mips.
# @example
# Helper.architecture('/bin/cat')
# #=> :amd64
def architecture(file)
return :invalid unless File.exist?(file)
@ -201,35 +204,58 @@ module OneGadget
end
# Present number in hex format.
# @param [Integer] val The number.
# @param [Boolean] psign Need to show plus sign when +val >= 0+.
# @return [String] string in hex format.
# @param [Integer] val
# The number.
# @param [Boolean] psign
# If needs to show the plus sign when +val >= 0+.
# @return [String]
# String in hex format.
# @example
# hex(32) #=> 0x20
# hex(32, psign: true) #=> +0x20
# hex(-40) #=> -0x28
# hex(0) #=> 0x0
# hex(0, psign: true) #=> +0x0
# Helper.hex(32) #=> '0x20'
# Helper.hex(32, psign: true) #=> '+0x20'
# Helper.hex(-40) #=> '-0x28'
# Helper.hex(0) #=> '0x0'
# Helper.hex(0, psign: true) #=> '+0x0'
def hex(val, psign: false)
return format("#{psign ? '+' : ''}0x%x", val) if val >= 0
format('-0x%x', -val)
end
# For checking a string is actually an integer.
# @param [String] str String to be checked.
# @return [Boolean] If +str+ can be converted into integer.
# Checks if a string can be converted into an integer.
# @param [String] str
# String to be checked.
# @return [Boolean]
# If +str+ can be converted into an integer.
# @example
# Helper.integer? '1234'
# # => true
# #=> true
# Helper.integer? '0x1234'
# # => true
# #=> true
# Helper.integer? '0xheapoverflow'
# # => false
# #=> false
def integer?(str)
true if Integer(str)
rescue ArgumentError, TypeError
false
end
# Cross-platform way of finding an executable in +$PATH+.
#
# @param [String] cmd
# @return [String?]
# @example
# Helper.which('ruby')
# #=> "/usr/bin/ruby"
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
end
end

View File

@ -2,7 +2,7 @@ require 'one_gadget/emulators/amd64'
describe OneGadget::Emulators::Amd64 do
before(:each) do
@processor = OneGadget::Emulators::Amd64.new
@processor = described_class.new
end
describe 'process' do

View File

@ -2,7 +2,7 @@ require 'one_gadget/emulators/i386'
describe OneGadget::Emulators::I386 do
before(:each) do
@processor = OneGadget::Emulators::I386.new
@processor = described_class.new
end
describe 'process' do

View File

@ -17,20 +17,20 @@ describe OneGadget::Emulators::Instruction do
it 'fetch_args' do
expect(@mov.fetch_args(<<-'EOS')).to eq ['rax', '[rip+0x2c16b4]']
d67e5: 48 8b 05 b4 16 2c 00 mov rax,QWORD PTR [rip+0x2c16b4] # 397ea0 <_DYNAMIC+0x340>
d67e5: 48 8b 05 b4 16 2c 00 mov rax,QWORD PTR [rip+0x2c16b4] # 397ea0 <_DYNAMIC+0x340>
EOS
expect(@mov.fetch_args(<<-'EOS')).to eq ['rdx', '[rax]']
d67f8: 48 8b 10 mov rdx,QWORD PTR [rax]
d67f8: 48 8b 10 mov rdx,QWORD PTR [rax]
EOS
expect { @mov.fetch_args('mov a, b, c') }.to raise_error(OneGadget::Error::ArgumentError)
expect(@lea.fetch_args(<<-'EOS')).to eq ['rsi', '[rsp+0x70]']
d67ec: 48 8d 74 24 70 lea rsi,[rsp+0x70]
d67ec: 48 8d 74 24 70 lea rsi,[rsp+0x70]
EOS
expect(@lea.fetch_args(<<-'EOS')).to eq ['rdi', '[rip+0x8ab61]']
d67f1: 48 8d 3d 61 ab 08 00 lea rdi,[rip+0x8ab61] # 161359 <_nl_POSIX_name+0x154>
d67f1: 48 8d 3d 61 ab 08 00 lea rdi,[rip+0x8ab61] # 161359 <_nl_POSIX_name+0x154>
EOS
expect(@call.fetch_args(<<-'EOS')).to eq ['b8470 <execve>']
d67fb: e8 70 1c fe ff call b8470 <execve>
d67fb: e8 70 1c fe ff call b8470 <execve>
EOS
end
end

View File

@ -1,16 +1,17 @@
require 'tempfile'
require 'one_gadget/gadget'
require 'one_gadget/helper'
describe OneGadget::Gadget do
before(:all) do
@build_id = 'fake_id'
OneGadget::Helper.color_off! # disable colorize for easy testing.
OneGadget::Gadget.add(@build_id, 0x1234, constraints: ['[rsp+0x30] == NULL', 'rax == 0'],
effect: 'execve("/bin/sh", rsp+0x30, rax)')
end
after(:all) do
OneGadget::Gadget::ClassMethods::BUILDS.delete @build_id
OneGadget::Gadget::ClassMethods::BUILDS.delete(@build_id)
end
it 'inspect' do
@ -22,6 +23,17 @@ constraints:
EOS
end
it 'remote' do
id = 'remote_has_this'
allow(OneGadget::Helper).to receive(:remote_builds).and_return([id])
allow(OneGadget::Helper).to receive(:download_build).with(id) { Tempfile.new(['remote', '.rb']) }
expect { hook_logger { described_class.builds(id) } }.to output(<<-EOS).to_stdout
[OneGadget] The desired one-gadget can be found in lastest version!
Update with: $ gem update one_gadget && gem cleanup one_gadget
EOS
OneGadget::Gadget::ClassMethods::BUILDS.delete(id)
end
context 'builds_info' do
it 'normal' do
expect(described_class.builds_info('58c735bc7b19b0aeb395cce70cf63bd62ac75e4a').join("\n")).to eq <<-EOS.strip

View File

@ -2,44 +2,45 @@ require 'one_gadget/helper'
describe OneGadget::Helper do
before(:all) do
@libcpath = File.join(__dir__, 'data', 'libc-2.23-60131540dadc6796cab33388349e6e4e68692053.so')
@libcpath = data_path('libc-2.23-60131540dadc6796cab33388349e6e4e68692053.so')
end
it 'abspath' do
expect(OneGadget::Helper.abspath('./spec/data/libc-2.23-60131540dadc6796cab33388349e6e4e68692053.so'))
expect(described_class.abspath('./spec/data/libc-2.23-60131540dadc6796cab33388349e6e4e68692053.so'))
.to eq @libcpath
end
it 'valid_elf_file?' do
expect(OneGadget::Helper.valid_elf_file?(__FILE__)).to be false
expect(OneGadget::Helper.valid_elf_file?(@libcpath)).to be true
expect(described_class.valid_elf_file?(__FILE__)).to be false
expect(described_class.valid_elf_file?(@libcpath)).to be true
end
it 'build_id_of' do
expect(OneGadget::Helper.build_id_of(@libcpath)).to eq '60131540dadc6796cab33388349e6e4e68692053'
expect(described_class.build_id_of(@libcpath)).to eq '60131540dadc6796cab33388349e6e4e68692053'
end
it 'colorize' do
allow(described_class).to receive(:color_enabled?).and_return(true)
expect(OneGadget::Helper.colorize('123', sev: :integer)).to eq "\e[38;5;189m123\e[0m"
expect(described_class.colorize('123', sev: :integer)).to eq "\e[38;5;189m123\e[0m"
end
it 'url_request' do
val = :val
expect { hook_logger { val = OneGadget::Helper.url_request('oao') } }.to output(<<-EOS.strip).to_stdout
expect { hook_logger { val = described_class.url_request('oao') } }.to output(<<-EOS.strip).to_stdout
[OneGadget] undefined method `request_uri' for #<URI::Generic oao>
EOS
expect(val).to be_nil
end
it 'architecture' do
expect(OneGadget::Helper.architecture(@libcpath)).to be :amd64
expect(OneGadget::Helper.architecture(__FILE__)).to be :invalid
expect(OneGadget::Helper.architecture(File.join(__dir__, 'data', 'aarch64-libc-2.24.so'))).to be :aarch64
# Just use for test unknown =~ =
expect(described_class.architecture(@libcpath)).to be :amd64
expect(described_class.architecture(__FILE__)).to be :invalid
expect(described_class.architecture(data_path('aarch64-libc-2.24.so'))).to be :aarch64
# for testing 'unknown'
Tempfile.create(['tmp', '.elf']) do |f|
f.write("\x7fELF\x02\x01\x01" + "\x00" * 9 + "\x01" * 48)
f.close
expect(OneGadget::Helper.architecture(f.path)).to be :unknown
expect(described_class.architecture(f.path)).to be :unknown
end
end
end

View File

@ -1,38 +1,32 @@
require 'mkmf'
require 'one_gadget'
require 'one_gadget/error'
describe 'one_gadget_amd64' do
before(:all) do
@data_path = ->(file) { File.join(__dir__, 'data', file) }
end
describe 'from file' do
before(:each) do
skip 'binutils not installed' if find_executable0('objdump').nil?
skip_unless_objdump
end
it 'libc-2.19' do
path = @data_path['libc-2.19-cf699a15caae64f50311fc4655b86dc39a479789.so']
path = data_path('libc-2.19-cf699a15caae64f50311fc4655b86dc39a479789.so')
expect(OneGadget.gadgets(file: path, force_file: true, level: 1))
.to eq [0x46428, 0x4647c, 0xc1ba3, 0xc1bf2, 0xe4968, 0xe5765, 0xe5771, 0xe66bd]
end
it 'libc-2.24' do
path = @data_path['libc-2.24-8cba3297f538691eb1875be62986993c004f3f4d.so']
path = data_path('libc-2.24-8cba3297f538691eb1875be62986993c004f3f4d.so')
expect(OneGadget.gadgets(file: path, force_file: true)).to eq [0x3f356, 0x3f3aa, 0xd67e5]
expect(one_gadget(path)).to eq OneGadget.gadgets(file: path)
end
it 'libc-2.26' do
path = @data_path['libc-2.26-ddcc13122ddbfe5e5ef77d4ebe66d124ae5762c2.so']
path = data_path('libc-2.26-ddcc13122ddbfe5e5ef77d4ebe66d124ae5762c2.so')
expect(OneGadget.gadgets(file: path, force_file: true)).to eq [0x47c46, 0x47c9a, 0xfccde, 0xfdb8e]
expect(one_gadget(path)).to eq OneGadget.gadgets(file: path)
end
it 'libc-2.27' do
path = @data_path['libc-2.27-b417c0ba7cc5cf06d1d1bed6652cedb9253c60d0.so']
path = data_path('libc-2.27-b417c0ba7cc5cf06d1d1bed6652cedb9253c60d0.so')
expect(OneGadget.gadgets(file: path, force_file: true)).to eq [0x4f2c5, 0x4f322, 0x10a38c]
expect(one_gadget(path)).to eq OneGadget.gadgets(file: path)
end
@ -72,11 +66,9 @@ describe 'one_gadget_amd64' do
it 'fetch from remote' do
entry = OneGadget::Gadget::ClassMethods::BUILDS.delete(@build_id)
OneGadget::Gadget::ClassMethods::BUILDS[:a] = 1
# silence the logger
allow(OneGadget::Logger).to receive(:ask_update)
expect(OneGadget.gadgets(build_id: @build_id)).not_to be_empty
OneGadget::Gadget::ClassMethods::BUILDS.delete(:a)
OneGadget::Gadget::ClassMethods::BUILDS[@build_id] = entry unless entry.nil?
end

View File

@ -1,47 +1,44 @@
require 'mkmf'
require 'one_gadget'
describe 'one_gadget_i386' do
before(:each) do
@build_id = '926eb99d49cab2e5622af38ab07395f5b32035e9'
@data_path = ->(file) { File.join(__dir__, 'data', file) }
end
describe 'from file' do
before(:each) do
skip 'binutils not installed' if find_executable0('objdump').nil?
skip_unless_objdump
end
it 'libc-2.19' do
path = @data_path['libc-2.19-fd51b20e670e9a9f60dc3b06dc9761fb08c9358b.so']
path = data_path('libc-2.19-fd51b20e670e9a9f60dc3b06dc9761fb08c9358b.so')
expect(OneGadget.gadgets(file: path, force_file: true)).to eq [0x3fd27, 0x64c64, 0x64c6a, 0x64c6e]
end
it 'libc-2.23' do
ans = [0x3ac5c, 0x3ac5e, 0x3ac62, 0x3ac69, 0x5fbc5, 0x5fbc6]
path = @data_path['libc-2.23-926eb99d49cab2e5622af38ab07395f5b32035e9.so']
path = data_path('libc-2.23-926eb99d49cab2e5622af38ab07395f5b32035e9.so')
expect(OneGadget.gadgets(file: path, force_file: true)).to eq ans
end
it 'libc-2.26' do
ans = [0x3cc2f, 0x3cc31, 0x3cc35, 0x3cc3c, 0x66e7f, 0x66e80, 0x132fbe, 0x132fbf]
path = @data_path['libc-2.26-f65648a832414f2144ce795d75b6045a1ec2e252.so']
path = data_path('libc-2.26-f65648a832414f2144ce795d75b6045a1ec2e252.so')
expect(OneGadget.gadgets(file: path, force_file: true)).to eq ans
end
it 'libc-2.27' do
ans = [0x3cbea, 0x3cbec, 0x3cbf0, 0x3cbf7, 0x6729f, 0x672a0, 0x13573e, 0x13573f]
path = @data_path['libc-2.27-63b3d43ad45e1b0f601848c65b067f9e9b40528b.so']
path = data_path('libc-2.27-63b3d43ad45e1b0f601848c65b067f9e9b40528b.so')
expect(OneGadget.gadgets(file: path, force_file: true)).to eq ans
end
it 'special filename' do
path = File.join(__dir__, 'data', 'filename$with+special&keys')
expect(OneGadget.gadgets(file: path)).not_to be_empty
expect(OneGadget.gadgets(file: data_path('filename$with+special&keys'))).not_to be_empty
end
end
describe 'from build id' do
before(:all) do
@build_id = '926eb99d49cab2e5622af38ab07395f5b32035e9'
end
it 'normal' do
# only check not empty because the gadgets might add frequently.
expect(OneGadget.gadgets(build_id: @build_id)).not_to be_empty

View File

@ -1,6 +1,8 @@
require 'one_gadget/helper'
require 'simplecov'
require 'one_gadget/helper'
require 'one_gadget/logger'
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
[SimpleCov::Formatter::HTMLFormatter]
)
@ -9,17 +11,17 @@ SimpleCov.start do
end
module Helpers
def hook_logger(&_block)
require 'one_gadget/logger'
def hook_logger
OneGadget::Logger.instance_variable_get(:@logger).reopen($stdout)
yield
end
# no method 'reopen' before ruby 2.3
org_logger = OneGadget::Logger.instance_variable_get(:@logger)
new_logger = ::Logger.new($stdout)
new_logger.formatter = org_logger.formatter
OneGadget::Logger.instance_variable_set(:@logger, new_logger)
ret = yield
OneGadget::Logger.instance_variable_set(:@logger, org_logger)
ret
def skip_unless_objdump
skip 'binutils not installed' if OneGadget::Helper.which('objdump').nil?
end
def data_path(file)
File.join(__dir__, 'data', file)
end
end