Upgrade rubocop to 0.59 (#44)

This commit is contained in:
david942j 2018-09-13 10:27:46 +08:00 committed by GitHub
parent 2ba8458e19
commit bc35ada51e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 60 additions and 3 deletions

View File

@ -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)
@ -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)

View File

@ -52,6 +52,7 @@ module OneGadget
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
@ -59,6 +60,7 @@ module OneGadget
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 }

View File

@ -26,6 +26,7 @@ module OneGadget
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

View File

@ -25,6 +25,7 @@ module OneGadget
# @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
@ -53,6 +54,7 @@ module OneGadget
# @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
@ -84,6 +86,7 @@ module OneGadget
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
@ -107,10 +110,12 @@ module OneGadget
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)

View File

@ -22,6 +22,7 @@ module OneGadget
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

View File

@ -31,6 +31,7 @@ module OneGadget
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
@ -87,8 +88,10 @@ module OneGadget
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
@ -128,9 +131,11 @@ module OneGadget
# 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
@ -146,12 +151,14 @@ module OneGadget
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
@ -164,6 +171,7 @@ module OneGadget
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
@ -183,6 +191,7 @@ module OneGadget
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' => {},
@ -192,6 +201,7 @@ module OneGadget
}
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
@ -210,6 +220,7 @@ module OneGadget
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

View File

@ -31,6 +31,7 @@ module OneGadget
i386: OneGadget::Fetcher::I386
}[arch]
raise Error::UnsupportedArchitectureError, arch if klass.nil?
trim_gadgets(klass.new(file).find)
end

View File

@ -16,6 +16,7 @@ module OneGadget
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
@ -34,6 +35,7 @@ module OneGadget
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]+:'`

View File

@ -24,6 +24,7 @@ module OneGadget
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
@ -69,6 +70,7 @@ module OneGadget
# 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')
@ -84,11 +86,13 @@ module OneGadget
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
@ -99,6 +103,7 @@ module OneGadget
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"
@ -110,9 +115,11 @@ module OneGadget
# 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
@ -125,6 +132,7 @@ module OneGadget
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(', ')})) }

View File

@ -13,6 +13,7 @@ module OneGadget
rel_sh_hex = rel_sh.to_s(16)
super do |candidate|
next false unless candidate.include?(rel_sh_hex)
true
end
end
@ -26,10 +27,12 @@ module OneGadget
# 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

View File

@ -54,9 +54,11 @@ module OneGadget
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)
@ -79,8 +81,10 @@ module OneGadget
# # ...
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|

View File

@ -23,6 +23,7 @@ module OneGadget
# @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
@ -71,6 +72,7 @@ module OneGadget
# @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
@ -102,6 +104,7 @@ module OneGadget
def color_enabled?
# if not set, use tty to check
return $stdout.tty? if @disable_color.nil?
!@disable_color
end
@ -121,6 +124,7 @@ module OneGadget
# @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]}"
@ -173,6 +177,7 @@ module OneGadget
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
@ -219,6 +224,7 @@ module OneGadget
# hex(0, psign: true) #=> +0x0
def hex(val, psign: false)
return format("#{psign ? '+' : ''}0x%x", val) if val >= 0
format('-0x%x', -val)
end

View File

@ -10,6 +10,7 @@ module OneGadget
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

View File

@ -18,6 +18,7 @@ module OneGadget
# @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")
@ -39,12 +40,14 @@ module OneGadget
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

View File

@ -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

View File

@ -18,12 +18,16 @@ namespace :builds do
info = libc_info(libc_file)
next failed('parse info fail') if info.nil? # error when fetching info
next failed('build id not found') if info[:build_id].nil? # no .note.gnu.build.id section
version = info[:info].scan(/version ([\d.]+\d)/).flatten.first
next skipped('version too old') if Gem::Version.new(version) < Gem::Version.new('2.19')
filename = File.join(path, "libc-#{version}-#{info[:build_id]}.rb")
next skipped('file exists') if File.file?(filename)
gadgets = OneGadget.gadgets(file: libc_file, force_file: true, details: true, level: 100)
next failed('no gadgets found') if gadgets.empty?
content = template(info, gadgets)
File.open(filename, 'w') { |f| f.write(content) }
puts 'done'
@ -62,11 +66,14 @@ OneGadget::Gadget.add(build_id, OFFSET,
return nil unless ['Advanced Micro Devices X86-64', 'Intel 80386'].include?(arch)
# let's skip amd64 with 32bit, i.e. x32
return nil if arch.start_with?('Advanced') && libc.elf_class == 32
str = file.read
st = str.index('GNU C Library')
return nil if st.nil?
len = str[st..-1].index("\x00")
return nil if len.nil?
fname = filename.sub('../libcdb', 'https://gitlab.com/libcdb/libcdb/blob/master')
{
build_id: build_id,

View File

@ -1,6 +1,7 @@
desc 'To auto generate README.md from README.tpl'
task :readme do
next if ENV['CI']
@tpl = IO.binread('README.tpl')
def replace(prefix)
@ -12,6 +13,7 @@ task :readme do
replace('SHELL_OUTPUT_OF') do |cmd|
'$ ' + cmd + "\n" + `#{cmd}`.lines.map do |c|
next "#\n" if c.strip.empty?
'# ' + c
end.join
end