Upgrade rubocop to 0.59 (#44)
This commit is contained in:
parent
2ba8458e19
commit
bc35ada51e
|
@ -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)
|
||||
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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]+:'`
|
||||
|
|
|
@ -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(', ')})) }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue