Skip to content

Commit

Permalink
Upgrade rubocop to 0.59 (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
david942j authored Sep 13, 2018
1 parent 2ba8458 commit bc35ada
Show file tree
Hide file tree
Showing 17 changed files with 60 additions and 3 deletions.
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ GEM
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.8.0)
rspec-support (3.8.0)
rubocop (0.58.2)
rubocop (0.59.0)
jaro_winkler (~> 1.5.1)
parallel (~> 1.10)
parser (>= 2.5, != 2.5.1.1)
Expand All @@ -58,7 +58,7 @@ DEPENDENCIES
one_gadget!
rake (~> 12.3)
rspec (~> 3.7)
rubocop (~> 0.58)
rubocop (~> 0.59)
simplecov (~> 0.16.1)
yard (~> 0.9)

Expand Down
2 changes: 2 additions & 0 deletions lib/one_gadget.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,15 @@ def from_file(path, force: false)
def try_from_build(file)
build_id = OneGadget::Helper.build_id_of(file)
return unless build_id

OneGadget::Fetcher.from_build_id(build_id, remote: false)
end

# Remove hard-to-reach-constrains gadgets according to level
def refine_gadgets(gadgets, level)
return [] if gadgets.empty?
return gadgets if level > 0 # currently only supports level > 0 or not

# remain gadgets with the fewest constraints
best = gadgets.map { |g| g.constraints.size }.min
gadgets.select { |g| g.constraints.size == best }
Expand Down
1 change: 1 addition & 0 deletions lib/one_gadget/emulators/instruction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def fetch_args(cmd)
if argc >= 0 && args.size != argc
raise Error::ArgumentError, "Incorrect argument number in #{cmd}, expect: #{argc}"
end

args.map do |arg|
arg.gsub(/XMMWORD|QWORD|DWORD|WORD|BYTE|PTR/, '').strip
end
Expand Down
5 changes: 5 additions & 0 deletions lib/one_gadget/emulators/lambda.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def initialize(obj)
# @return [Lambda] The result.
def +(other)
raise Error::ArgumentError, 'Expect other to be Numeric.' unless other.is_a?(Numeric)

if deref_count > 0
ret = Lambda.new(self)
else
Expand Down Expand Up @@ -53,6 +54,7 @@ def deref!
# @raise [Error::ArgumentError] When this object cannot be referenced anymore.
def ref!
raise Error::ArgumentError, 'Cannot reference anymore!' if @deref_count <= 0

@deref_count -= 1
end

Expand Down Expand Up @@ -84,6 +86,7 @@ def to_s
def evaluate(context)
raise Error::ArgumentError, "Can't eval #{self}" if deref_count > 0
raise Error::ArgumentError, "Can't eval #{self}" if obj && !context.key?(obj)

context[obj] + immi
end

Expand All @@ -107,10 +110,12 @@ def parse(arg, predefined: {})
deref_count = 1
end
return Integer(arg) if OneGadget::Helper.integer?(arg)

sign = arg =~ /[+-]/
val = 0
if sign
raise Error::ArgumentError, "Not support #{arg}" unless OneGadget::Helper.integer?(arg[sign..-1])

val = Integer(arg.slice!(sign..-1))
end
obj = predefined[arg] || Lambda.new(arg)
Expand Down
1 change: 1 addition & 0 deletions lib/one_gadget/emulators/processor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def initialize(registers)
def parse(cmd)
inst = instructions.find { |i| i.match?(cmd) }
raise Error::ArgumentError, "Not implemented instruction in #{cmd}" if inst.nil?

[inst, inst.fetch_args(cmd)]
end

Expand Down
11 changes: 11 additions & 0 deletions lib/one_gadget/emulators/x86.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def process!(cmd)
inst, args = parse(cmd)
# return registers[pc] = args[0] if inst.inst == 'call'
return true if inst.inst == 'jmp' # believe the fetcher has handled jmp.

sym = "inst_#{inst.inst}".to_sym
__send__(sym, *args) != :fail
end
Expand Down Expand Up @@ -87,8 +88,10 @@ def inst_mov(tar, src)
else
# Just ignore strange case...
return unless tar.include?(sp)

tar = OneGadget::Emulators::Lambda.parse(tar, predefined: registers)
return if tar.deref_count != 1 # should not happen

tar.ref!
stack[tar.evaluate(eval_dict)] = src
end
Expand Down Expand Up @@ -128,9 +131,11 @@ def inst_movhps(tar, src)
# 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)

tar_lm = OneGadget::Emulators::Lambda.parse(tar, 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]
end
Expand All @@ -146,12 +151,14 @@ def inst_push(val)
registers[sp] -= size_t
cur_top = registers[sp].evaluate(eval_dict)
raise Error::ArgumentError, "Corrupted stack pointer: #{cur_top}" unless cur_top.is_a?(Integer)

stack[cur_top] = val
end

def inst_xor(dst, src)
# only supports dst == src
raise Error::ArgumentError, 'xor operator only supports dst = src' unless dst == src

dst[0] = 'r' if self.class.bits == 64 && dst.start_with?('e')
registers[dst] = 0
end
Expand All @@ -164,6 +171,7 @@ def inst_add(tar, src)
def inst_sub(tar, 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
end

Expand All @@ -183,6 +191,7 @@ def check_argument(idx, expect)
def inst_call(addr)
# This is the last call
return registers[pc] = addr if %w[execve execl].any? { |n| addr.include?(n) }

# TODO: handle some registers would be fucked after call
checker = {
'sigprocmask' => {},
Expand All @@ -192,6 +201,7 @@ def inst_call(addr)
}
func = checker.keys.find { |n| addr.include?(n) }
return if func && checker[func].all? { |idx, sym| check_argument(idx, sym) }

# unhandled case or checker's condition fails
:fail
end
Expand All @@ -210,6 +220,7 @@ def raise_unsupported(inst, *args)

def to_lambda(reg)
return super unless reg =~ /^xmm\d+$/

Array.new(128 / self.class.bits) do |i|
OneGadget::Emulators::Lambda.new("#{reg}__#{i}")
end
Expand Down
1 change: 1 addition & 0 deletions lib/one_gadget/fetcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def from_file(file)
i386: OneGadget::Fetcher::I386
}[arch]
raise Error::UnsupportedArchitectureError, arch if klass.nil?

trim_gadgets(klass.new(file).find)
end

Expand Down
2 changes: 2 additions & 0 deletions lib/one_gadget/fetchers/amd64.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def candidates
cands = super do |candidate|
next false unless candidate.include?(bin_sh_hex) # works in x86-64
next false unless candidate.lines.last.include?('execve') # only care execve

true
end
cands + jmp_case_candidates # + sigaction_case_candidates
Expand All @@ -34,6 +35,7 @@ def jmp_case_candidates
cand = cand.lines.map(&:strip).reject(&:empty?)
jmp_at = cand.index { |c| c.include?('jmp') }
next nil if jmp_at.nil?

cand = cand[0..jmp_at]
jmp_addr = cand.last.scan(/jmp\s+([\da-f]+)\s/)[0][0].to_i(16)
dump = `#{objdump_cmd(start: jmp_addr, stop: jmp_addr + 100)}|egrep '[0-9a-f]+:'`
Expand Down
8 changes: 8 additions & 0 deletions lib/one_gadget/fetchers/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def find
processor = emulate(lines[i..-1])
options = resolve(processor)
next if options.nil? # impossible be a gadget

offset = offset_of(lines[i])
gadgets << OneGadget::Gadget::Gadget.new(offset, options)
end
Expand Down Expand Up @@ -69,6 +70,7 @@ def resolve(processor)
# since the logic is different between amd64 and i386,
# invoke str_bin_sh? for checking
return unless str_bin_sh?(processor.argument(0).to_s)

if call.include?('execve')
resolve_execve(processor)
elsif call.include?('execl')
Expand All @@ -84,11 +86,13 @@ def resolve_execve(processor)
cons = []
cons << check_execve_arg(processor, arg1)
return nil unless cons.all?

envp = 'environ'
return nil unless check_envp(processor, arg2) do |c|
cons << c
envp = arg2
end

{ constraints: cons, effect: %(execve("/bin/sh", #{arg1}, #{envp})) }
end

Expand All @@ -99,6 +103,7 @@ def check_execve_arg(processor, arg)
num = Integer(arg[processor.sp.size..-1])
slot = processor.stack[num].to_s
return if global_var?(slot)

"#{slot} == NULL"
else
"[#{arg}] == NULL || #{arg} == NULL"
Expand All @@ -110,9 +115,11 @@ def check_envp(processor, arg)
# believe it is environ
# if starts with [[ but not global, drop it.
return global_var?(arg) if arg.start_with?('[[')

# normal
cons = check_execve_arg(processor, arg)
return nil if cons.nil?

yield cons
end

Expand All @@ -125,6 +132,7 @@ def resolve_execl(processor)
args << '"sh"'
end
return nil if global_var?(arg) # we don't want base-related constraints

args << arg
# now arg is the constraint.
{ constraints: ["#{arg} == NULL"], effect: %(execl("/bin/sh", #{args.join(', ')})) }
Expand Down
3 changes: 3 additions & 0 deletions lib/one_gadget/fetchers/i386.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def candidates
rel_sh_hex = rel_sh.to_s(16)
super do |candidate|
next false unless candidate.include?(rel_sh_hex)

true
end
end
Expand All @@ -26,10 +27,12 @@ def resolve(processor)
# first check if argument 0 is '/bin/sh' to prevent error
arg0 = processor.argument(0)
return nil unless str_bin_sh?(arg0.to_s)

@base_reg = arg0.deref.obj.to_s # this should be esi or ebx..
# now we can let parent to invoke global_var?
res = super
return if res.nil?

# unshift got constraint into cons
res[:constraints].unshift("#{@base_reg} is the GOT address of libc")
res
Expand Down
4 changes: 4 additions & 0 deletions lib/one_gadget/gadget.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@ def builds(build_id, remote: true)
require_all if BUILDS.empty?
return BUILDS[build_id] if BUILDS.key?(build_id)
return build_not_found unless remote

# fetch remote builds
table = OneGadget::Helper.remote_builds.find { |c| c.include?(build_id) }
return build_not_found if table.nil? # remote doesn't have this one either.

# builds found in remote! Ask update gem and download remote gadgets.
OneGadget::Helper.ask_update(msg: 'The desired one-gadget can be found in lastest version!')
tmp_file = OneGadget::Helper.download_build(table)
Expand All @@ -79,8 +81,10 @@ def builds(build_id, remote: true)
# # ...
def builds_info(build_id)
raise Error::ArgumentError, "Invalid BuildID #{build_id.inspect}" if build_id =~ /[^0-9a-f]/

files = Dir.glob(File.join(BUILDS_PATH, "*-#{build_id}*.rb")).sort
return OneGadget::Logger.not_found(build_id) && nil if files.empty?

if files.size > 1
OneGadget::Logger.warn("Multiple BuildIDs match /^#{build_id}/\n")
show = files.map do |f|
Expand Down
6 changes: 6 additions & 0 deletions lib/one_gadget/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ module ClassMethods
# @return [void]
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

Expand Down Expand Up @@ -71,6 +72,7 @@ def valid_elf_file?(path)
# @raise [Error::ArgumentError] Raise exception if not a valid ELF.
def verify_elf_file!(path)
return if valid_elf_file?(path)

raise Error::ArgumentError, 'Not an ELF file, expected glibc as input'
end

Expand Down Expand Up @@ -102,6 +104,7 @@ def color_on!
def color_enabled?
# if not set, use tty to check
return $stdout.tty? if @disable_color.nil?

!@disable_color
end

Expand All @@ -121,6 +124,7 @@ def color_enabled?
# @return [String] Wrapper with color codes.
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]}"
Expand Down Expand Up @@ -173,6 +177,7 @@ def url_request(url)

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
p e
Expand Down Expand Up @@ -219,6 +224,7 @@ def architecture(file)
# hex(0, psign: true) #=> +0x0
def hex(val, psign: false)
return format("#{psign ? '+' : ''}0x%x", val) if val >= 0

format('-0x%x', -val)
end

Expand Down
1 change: 1 addition & 0 deletions lib/one_gadget/logger.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module Logger
prep = ' ' * 12
message = msg.lines.map.with_index do |str, i|
next str if i.zero?

str.strip.empty? ? str : prep + str
end
color = case severity
Expand Down
3 changes: 3 additions & 0 deletions lib/one_gadget/update.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class << self
# @return [void]
def check!
return unless need_check?

FileUtils.touch(cache_file)
OneGadget::Logger.info("Checking for new versions of OneGadget\n" \
"To disable this functionality, do\n$ echo never > #{CACHE_FILE}\n\n")
Expand All @@ -39,12 +40,14 @@ def need_check?
cache = cache_file
return false if cache.nil? # cache file fails, no update check.
return false if IO.binread(cache).strip == 'never'

Time.now >= last_check + FREQUENCY
end

def last_check
cache = cache_file
return Time.now if cache.nil?

File.open(cache, &:mtime)
end

Expand Down
2 changes: 1 addition & 1 deletion one_gadget.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Gem::Specification.new do |s|

s.add_development_dependency 'rake', '~> 12.3'
s.add_development_dependency 'rspec', '~> 3.7'
s.add_development_dependency 'rubocop', '~> 0.58'
s.add_development_dependency 'rubocop', '~> 0.59'
s.add_development_dependency 'simplecov', '~> 0.16.1'
s.add_development_dependency 'yard', '~> 0.9'
end
Loading

0 comments on commit bc35ada

Please sign in to comment.