pwntools 0.1.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +96 -15
- data/Rakefile +8 -2
- data/lib/pwn.rb +10 -7
- data/lib/pwnlib/abi.rb +61 -0
- data/lib/pwnlib/asm.rb +357 -0
- data/lib/pwnlib/constants/constant.rb +19 -3
- data/lib/pwnlib/constants/constants.rb +46 -20
- data/lib/pwnlib/constants/linux/amd64.rb +32 -1
- data/lib/pwnlib/constants/linux/i386.rb +2 -0
- data/lib/pwnlib/context.rb +128 -27
- data/lib/pwnlib/dynelf.rb +122 -54
- data/lib/pwnlib/elf/elf.rb +340 -0
- data/lib/pwnlib/errors.rb +31 -0
- data/lib/pwnlib/ext/array.rb +2 -1
- data/lib/pwnlib/ext/helper.rb +6 -5
- data/lib/pwnlib/ext/integer.rb +2 -1
- data/lib/pwnlib/ext/string.rb +3 -2
- data/lib/pwnlib/logger.rb +245 -0
- data/lib/pwnlib/memleak.rb +59 -29
- data/lib/pwnlib/pwn.rb +27 -9
- data/lib/pwnlib/reg_sort.rb +109 -110
- data/lib/pwnlib/runner.rb +53 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/common.rb +16 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/infloop.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/memcpy.rb +35 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/mov.rb +131 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/nop.rb +18 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/popad.rb +28 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/pushstr.rb +66 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/pushstr_array.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/ret.rb +33 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/setregs.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/cat.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/execve.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/exit.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/linux.rb +16 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/ls.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/open.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/sh.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/sleep.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/syscall.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/helper.rb +115 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/common.rb +16 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/infloop.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/memcpy.rb +34 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/mov.rb +93 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/nop.rb +18 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/pushstr.rb +41 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/pushstr_array.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/setregs.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/cat.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/execve.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/exit.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/linux.rb +16 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/ls.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/open.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/sh.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/sleep.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/syscall.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/common.rb +29 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/infloop.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/memcpy.rb +17 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/mov.rb +17 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/pushstr.rb +17 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/pushstr_array.rb +86 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/setregs.rb +84 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/cat.rb +54 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/execve.rb +72 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/exit.rb +34 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/linux.rb +16 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/ls.rb +67 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/open.rb +47 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/sh.rb +53 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/sleep.rb +52 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/syscall.rb +52 -0
- data/lib/pwnlib/shellcraft/registers.rb +148 -0
- data/lib/pwnlib/shellcraft/shellcraft.rb +73 -0
- data/lib/pwnlib/timer.rb +67 -0
- data/lib/pwnlib/tubes/buffer.rb +99 -0
- data/lib/pwnlib/tubes/process.rb +155 -0
- data/lib/pwnlib/tubes/serialtube.rb +114 -0
- data/lib/pwnlib/tubes/sock.rb +101 -0
- data/lib/pwnlib/tubes/tube.rb +442 -0
- data/lib/pwnlib/ui.rb +21 -0
- data/lib/pwnlib/util/cyclic.rb +97 -94
- data/lib/pwnlib/util/fiddling.rb +288 -220
- data/lib/pwnlib/util/getdents.rb +85 -0
- data/lib/pwnlib/util/hexdump.rb +116 -112
- data/lib/pwnlib/util/lists.rb +58 -0
- data/lib/pwnlib/util/packing.rb +223 -228
- data/lib/pwnlib/util/ruby.rb +19 -0
- data/lib/pwnlib/version.rb +3 -1
- data/test/abi_test.rb +22 -0
- data/test/asm_test.rb +177 -0
- data/test/constants/constant_test.rb +2 -0
- data/test/constants/constants_test.rb +5 -2
- data/test/context_test.rb +14 -3
- data/test/data/assembly/aarch64.s +19 -0
- data/test/data/assembly/amd64.s +21 -0
- data/test/data/assembly/arm.s +9 -0
- data/test/data/assembly/i386.s +21 -0
- data/test/data/assembly/mips.s +16 -0
- data/test/data/assembly/mips64.s +6 -0
- data/test/data/assembly/powerpc.s +18 -0
- data/test/data/assembly/powerpc64.s +36 -0
- data/test/data/assembly/sparc.s +33 -0
- data/test/data/assembly/sparc64.s +5 -0
- data/test/data/assembly/thumb.s +37 -0
- data/test/data/echo.rb +16 -0
- data/test/data/elfs/Makefile +24 -0
- data/test/data/elfs/amd64.frelro.elf +0 -0
- data/test/data/elfs/amd64.frelro.pie.elf +0 -0
- data/test/data/elfs/amd64.nrelro.elf +0 -0
- data/test/data/elfs/amd64.prelro.elf +0 -0
- data/test/data/elfs/amd64.static.elf +0 -0
- data/test/data/elfs/i386.frelro.pie.elf +0 -0
- data/test/data/elfs/i386.prelro.elf +0 -0
- data/test/data/elfs/source.cpp +19 -0
- data/test/data/flag +1 -0
- data/test/data/lib32/ld.so.2 +0 -0
- data/test/data/lib32/libc.so.6 +0 -0
- data/test/data/lib64/ld.so.2 +0 -0
- data/test/data/lib64/libc.so.6 +0 -0
- data/test/dynelf_test.rb +62 -25
- data/test/elf/elf_test.rb +147 -0
- data/test/ext_test.rb +4 -2
- data/test/files/use_pwn.rb +3 -6
- data/test/files/use_pwnlib.rb +2 -1
- data/test/full_file_test.rb +6 -0
- data/test/logger_test.rb +120 -0
- data/test/memleak_test.rb +5 -33
- data/test/reg_sort_test.rb +4 -1
- data/test/runner_test.rb +32 -0
- data/test/shellcraft/infloop_test.rb +27 -0
- data/test/shellcraft/linux/cat_test.rb +87 -0
- data/test/shellcraft/linux/ls_test.rb +109 -0
- data/test/shellcraft/linux/sh_test.rb +120 -0
- data/test/shellcraft/linux/sleep_test.rb +68 -0
- data/test/shellcraft/linux/syscalls/execve_test.rb +137 -0
- data/test/shellcraft/linux/syscalls/exit_test.rb +57 -0
- data/test/shellcraft/linux/syscalls/open_test.rb +87 -0
- data/test/shellcraft/linux/syscalls/syscall_test.rb +84 -0
- data/test/shellcraft/memcpy_test.rb +50 -0
- data/test/shellcraft/mov_test.rb +99 -0
- data/test/shellcraft/nop_test.rb +27 -0
- data/test/shellcraft/popad_test.rb +30 -0
- data/test/shellcraft/pushstr_array_test.rb +92 -0
- data/test/shellcraft/pushstr_test.rb +109 -0
- data/test/shellcraft/registers_test.rb +33 -0
- data/test/shellcraft/ret_test.rb +31 -0
- data/test/shellcraft/setregs_test.rb +63 -0
- data/test/shellcraft/shellcraft_test.rb +30 -0
- data/test/test_helper.rb +61 -2
- data/test/timer_test.rb +42 -0
- data/test/tubes/buffer_test.rb +46 -0
- data/test/tubes/process_test.rb +105 -0
- data/test/tubes/serialtube_test.rb +162 -0
- data/test/tubes/sock_test.rb +68 -0
- data/test/tubes/tube_test.rb +320 -0
- data/test/ui_test.rb +18 -0
- data/test/util/cyclic_test.rb +3 -1
- data/test/util/fiddling_test.rb +12 -3
- data/test/util/getdents_test.rb +33 -0
- data/test/util/hexdump_test.rb +9 -10
- data/test/util/lists_test.rb +22 -0
- data/test/util/packing_test.rb +5 -3
- metadata +357 -37
data/lib/pwnlib/pwn.rb
CHANGED
@@ -1,26 +1,44 @@
|
|
1
1
|
# encoding: ASCII-8BIT
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
# require this file would also require all things in pwnlib, but would not
|
4
|
-
# pollute anything.
|
4
|
+
# require this file would also require all things in pwnlib, but would not pollute anything.
|
5
5
|
|
6
|
+
require 'pwnlib/asm'
|
6
7
|
require 'pwnlib/constants/constant'
|
7
8
|
require 'pwnlib/constants/constants'
|
8
9
|
require 'pwnlib/context'
|
9
10
|
require 'pwnlib/dynelf'
|
11
|
+
require 'pwnlib/elf/elf'
|
12
|
+
require 'pwnlib/errors'
|
13
|
+
require 'pwnlib/logger'
|
10
14
|
require 'pwnlib/reg_sort'
|
11
|
-
|
15
|
+
require 'pwnlib/runner'
|
16
|
+
require 'pwnlib/shellcraft/shellcraft'
|
17
|
+
require 'pwnlib/tubes/process'
|
18
|
+
require 'pwnlib/tubes/serialtube'
|
19
|
+
require 'pwnlib/tubes/sock'
|
20
|
+
require 'pwnlib/ui'
|
12
21
|
require 'pwnlib/util/cyclic'
|
13
22
|
require 'pwnlib/util/fiddling'
|
23
|
+
require 'pwnlib/util/getdents'
|
14
24
|
require 'pwnlib/util/hexdump'
|
25
|
+
require 'pwnlib/util/lists'
|
15
26
|
require 'pwnlib/util/packing'
|
16
27
|
|
17
|
-
# include this module in a class to use all pwnlib functions in that class
|
18
|
-
# instance.
|
28
|
+
# include this module in a class to use all pwnlib functions in that class instance.
|
19
29
|
module Pwn
|
30
|
+
include ::Pwnlib::Asm
|
20
31
|
include ::Pwnlib::Context
|
32
|
+
include ::Pwnlib::Logger
|
33
|
+
include ::Pwnlib::Runner
|
34
|
+
include ::Pwnlib::UI
|
35
|
+
include ::Pwnlib::Util::Cyclic
|
36
|
+
include ::Pwnlib::Util::Fiddling
|
37
|
+
include ::Pwnlib::Util::HexDump
|
38
|
+
include ::Pwnlib::Util::Lists
|
39
|
+
include ::Pwnlib::Util::Packing
|
21
40
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
include ::Pwnlib::Util::Packing::ClassMethods
|
41
|
+
def shellcraft
|
42
|
+
::Pwnlib::Shellcraft::Shellcraft.instance
|
43
|
+
end
|
26
44
|
end
|
data/lib/pwnlib/reg_sort.rb
CHANGED
@@ -1,127 +1,126 @@
|
|
1
1
|
# encoding: ASCII-8BIT
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'pwnlib/context'
|
5
|
+
require 'pwnlib/util/ruby'
|
4
6
|
|
5
7
|
module Pwnlib
|
6
8
|
# Do topological sort on register assignments.
|
7
9
|
module RegSort
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
10
|
+
module_function
|
11
|
+
|
12
|
+
# Sorts register dependencies.
|
13
|
+
#
|
14
|
+
# Given a dictionary of registers to desired register contents, return the optimal order in which to set the
|
15
|
+
# registers to those contents.
|
16
|
+
#
|
17
|
+
# The implementation assumes that it is possible to move from any register to any other register.
|
18
|
+
#
|
19
|
+
# @param [Hash<Symbol, String => Object>] in_out
|
20
|
+
# Dictionary of desired register states.
|
21
|
+
# Keys are registers, values are either registers or any other value.
|
22
|
+
# @param [Array<String>] all_regs
|
23
|
+
# List of all possible registers.
|
24
|
+
# Used to determine which values in +in_out+ are registers, versus regular values.
|
25
|
+
# @param [Boolean] randomize
|
26
|
+
# Randomize as much as possible about the order or registers.
|
27
|
+
#
|
28
|
+
# @return [Array]
|
29
|
+
# Array of instructions, see examples for more details.
|
30
|
+
#
|
31
|
+
# @diff We don't support +tmp+/+xchg+ options because there's no such usage at all.
|
32
|
+
#
|
33
|
+
# @example
|
34
|
+
# regs = %w(a b c d x y z)
|
35
|
+
# regsort({a: 1, b: 2}, regs)
|
36
|
+
# #=> [['mov', 'a', 1], ['mov', 'b', 2]]
|
37
|
+
# regsort({a: 'b', b: 'a'}, regs)
|
38
|
+
# #=> [['xchg', 'a', 'b']]
|
39
|
+
# regsort({a: 1, b: 'a'}, regs)
|
40
|
+
# #=> [['mov', 'b', 'a'], ['mov', 'a', 1]]
|
41
|
+
# regsort({a: 'b', b: 'a', c: 3}, regs)
|
42
|
+
# #=> [['mov', 'c', 3], ['xchg', 'a', 'b']]
|
43
|
+
# regsort({a: 'b', b: 'a', c: 'b'}, regs)
|
44
|
+
# #=> [['mov', 'c', 'b'], ['xchg', 'a', 'b']]
|
45
|
+
# regsort({a: 'b', b: 'c', c: 'a', x: '1', y: 'z', z: 'c'}, regs)
|
46
|
+
# #=> [['mov', 'x', '1'],
|
47
|
+
# ['mov', 'y', 'z'],
|
48
|
+
# ['mov', 'z', 'c'],
|
49
|
+
# ['xchg', 'a', 'b'],
|
50
|
+
# ['xchg', 'b', 'c']]
|
51
|
+
def regsort(in_out, all_regs, randomize: nil)
|
52
|
+
# randomize = context.randomize if randomize.nil?
|
53
|
+
|
54
|
+
# TODO(david942j): stringify_keys
|
55
|
+
in_out = in_out.transform_keys(&:to_s)
|
56
|
+
# Drop all registers which will be set to themselves.
|
57
|
+
# Ex. {eax: 'eax'}
|
58
|
+
in_out.reject! { |k, v| k == v }
|
59
|
+
|
60
|
+
# Check input
|
61
|
+
if (in_out.keys - all_regs).any?
|
62
|
+
raise ArgumentError, format('Unknown register! Know: %p. Got: %p', all_regs, in_out)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Collapse constant values.
|
50
66
|
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
in_out = in_out.map { |k, v| [k.to_s, v] }.to_h
|
59
|
-
# Drop all registers which will be set to themselves.
|
60
|
-
# Ex. {eax: 'eax'}
|
61
|
-
in_out.reject! { |k, v| k == v }
|
62
|
-
|
63
|
-
# Check input
|
64
|
-
if (in_out.keys - all_regs).any?
|
65
|
-
raise ArgumentError, format('Unknown register! Know: %p. Got: %p', all_regs, in_out)
|
66
|
-
end
|
67
|
+
# Ex. {eax: 1, ebx: 1} can be collapsed to {eax: 1, ebx: 'eax'}.
|
68
|
+
# +post_mov+ are collapsed registers, set their values in the end.
|
69
|
+
post_mov = in_out.group_by { |_, v| v }.each_value.with_object({}) do |list, hash|
|
70
|
+
list.sort!
|
71
|
+
first_reg, val = list.shift
|
72
|
+
# Special case for val.zero? because zeroify registers is cheaper than mov.
|
73
|
+
next if list.empty? || all_regs.include?(val) || val.zero?
|
67
74
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
# +post_mov+ are collapsed registers, set their values in the end.
|
72
|
-
post_mov = in_out.group_by { |_, v| v }.each_value.with_object({}) do |list, hash|
|
73
|
-
list.sort!
|
74
|
-
first_reg, val = list.shift
|
75
|
-
# Special case for val.zero? because zeroify registers cost cheaper than mov.
|
76
|
-
next if list.empty? || all_regs.include?(val) || val.zero?
|
77
|
-
list.each do |reg, _|
|
78
|
-
hash[reg] = first_reg
|
79
|
-
in_out.delete(reg)
|
80
|
-
end
|
75
|
+
list.each do |(reg, _)|
|
76
|
+
hash[reg] = first_reg
|
77
|
+
in_out.delete(reg)
|
81
78
|
end
|
79
|
+
end
|
82
80
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
# Let's do the topological sort.
|
87
|
-
# so sad ruby 2.1 doesn't have +itself+...
|
88
|
-
deg = graph.values.group_by { |i| i }.map { |k, v| [k, v.size] }.to_h
|
89
|
-
graph.each_key { |k| deg[k] ||= 0 }
|
90
|
-
|
91
|
-
until deg.empty?
|
92
|
-
min_deg = deg.min_by { |_, v| v }[1]
|
93
|
-
break unless min_deg.zero? # remain are all cycles
|
94
|
-
min_pivs = deg.select { |_, v| v == min_deg }
|
95
|
-
piv = randomize ? min_pivs.sample : min_pivs.first
|
96
|
-
dst = piv.first
|
97
|
-
deg.delete(dst)
|
98
|
-
next unless graph.key?(dst) # Reach an end node.
|
99
|
-
deg[graph[dst]] -= 1
|
100
|
-
result << ['mov', dst, graph[dst]]
|
101
|
-
graph.delete(dst)
|
102
|
-
end
|
81
|
+
graph = in_out.dup
|
82
|
+
result = []
|
103
83
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
result << ['xchg', d, s]
|
109
|
-
end
|
110
|
-
cycle.each { |r| graph.delete(r) }
|
111
|
-
end
|
84
|
+
# Let's do the topological sort.
|
85
|
+
# so sad ruby 2.1 doesn't have +itself+...
|
86
|
+
deg = graph.values.group_by { |i| i }.transform_values(&:size)
|
87
|
+
graph.each_key { |k| deg[k] ||= 0 }
|
112
88
|
|
113
|
-
|
114
|
-
|
115
|
-
|
89
|
+
until deg.empty?
|
90
|
+
min_deg = deg.min_by { |_, v| v }[1]
|
91
|
+
break unless min_deg.zero? # remain are all cycles
|
92
|
+
|
93
|
+
min_pivs = deg.select { |_, v| v == min_deg }
|
94
|
+
piv = randomize ? min_pivs.sample : min_pivs.first
|
95
|
+
dst = piv.first
|
96
|
+
deg.delete(dst)
|
97
|
+
next unless graph.key?(dst) # Reach an end node.
|
98
|
+
|
99
|
+
deg[graph[dst]] -= 1
|
100
|
+
result << ['mov', dst, graph[dst]]
|
101
|
+
graph.delete(dst)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Remain must be cycles.
|
105
|
+
graph.each_key do |reg|
|
106
|
+
cycle = check_cycle(reg, graph)
|
107
|
+
cycle.each_cons(2) do |d, s|
|
108
|
+
result << ['xchg', d, s]
|
116
109
|
end
|
110
|
+
cycle.each { |r| graph.delete(r) }
|
111
|
+
end
|
117
112
|
|
118
|
-
|
113
|
+
# Now assign those collapsed registers.
|
114
|
+
post_mov.sort.each do |dreg, sreg|
|
115
|
+
result << ['mov', dreg, sreg]
|
119
116
|
end
|
120
117
|
|
121
|
-
|
118
|
+
result
|
119
|
+
end
|
122
120
|
|
123
|
-
|
124
|
-
# return the path walked if it is encountered again.
|
121
|
+
Pwnlib::Util::Ruby.private_class_method_block do
|
122
|
+
# Walk down the assignment list of a register, return the path walked if it is encountered again.
|
123
|
+
#
|
125
124
|
# @example
|
126
125
|
# check_cycle('a', {'a' => 1}) #=> []
|
127
126
|
# check_cycle('a', {'a' => 'a'}) #=> ['a']
|
@@ -132,16 +131,16 @@ module Pwnlib
|
|
132
131
|
check_cycle_(reg, assignments, [])
|
133
132
|
end
|
134
133
|
|
135
|
-
def check_cycle_(reg, assignments, path)
|
134
|
+
def check_cycle_(reg, assignments, path)
|
136
135
|
target = assignments[reg]
|
137
136
|
path << reg
|
138
|
-
# No cycle, some other value (e.g. 1)
|
137
|
+
# No cycle, some other value (e.g. 1).
|
139
138
|
return [] unless assignments.key?(target)
|
140
|
-
# Found a cycle
|
139
|
+
# Found a cycle.
|
141
140
|
return target == path.first ? path : [] if path.include?(target)
|
141
|
+
|
142
142
|
check_cycle_(target, assignments, path)
|
143
143
|
end
|
144
144
|
end
|
145
|
-
extend ClassMethods
|
146
145
|
end
|
147
146
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: ASCII-8BIT
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
require 'pwnlib/asm'
|
7
|
+
require 'pwnlib/tubes/process'
|
8
|
+
|
9
|
+
module Pwnlib
|
10
|
+
# This module collects the methods for executing codes, e.g., assembly code, assembled machine code, etc.
|
11
|
+
module Runner
|
12
|
+
module_function
|
13
|
+
|
14
|
+
# Given an assembly listing, assemble and execute it.
|
15
|
+
#
|
16
|
+
# @param [String] assembly
|
17
|
+
# Assembly code.
|
18
|
+
#
|
19
|
+
# @return [Pwnlib::Tubes::Process]
|
20
|
+
# The tube for interacting.
|
21
|
+
#
|
22
|
+
# @see Runner.run_shellcode
|
23
|
+
def run_assembly(assembly)
|
24
|
+
run_shellcode(::Pwnlib::Asm.asm(assembly))
|
25
|
+
end
|
26
|
+
|
27
|
+
# Given assembled machine code bytes, execute them.
|
28
|
+
#
|
29
|
+
# @param [String] bytes
|
30
|
+
# Assembled code.
|
31
|
+
#
|
32
|
+
# @return [Pwnlib::Tubes::Process]
|
33
|
+
# The tube for interacting.
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
# r = run_shellcode(asm(shellcraft.cat('/etc/passwd')))
|
37
|
+
# r.interact
|
38
|
+
# # [INFO] Switching to interactive mode
|
39
|
+
# # root:x:0:0:root:/root:/bin/bash
|
40
|
+
# # daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
|
41
|
+
# # bin:x:2:2:bin:/bin:/usr/sbin/nologin
|
42
|
+
# # sys:x:3:3:sys:/dev:/usr/sbin/nologin
|
43
|
+
# # sync:x:4:65534:sync:/bin:/bin/sync
|
44
|
+
# # games:x:5:60:games:/usr/games:/usr/sbin/nologin
|
45
|
+
# # [INFO] Got EOF in interactive mode
|
46
|
+
# #=> true
|
47
|
+
def run_shellcode(bytes)
|
48
|
+
file = ::Pwnlib::Asm.make_elf(bytes, to_file: true)
|
49
|
+
at_exit { FileUtils.rm_f(file) if File.exist?(file) }
|
50
|
+
::Pwnlib::Tubes::Process.new(file)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pwnlib/shellcraft/generators/helper'
|
4
|
+
|
5
|
+
module Pwnlib
|
6
|
+
module Shellcraft
|
7
|
+
module Generators
|
8
|
+
module Amd64
|
9
|
+
# For non os-related methods.
|
10
|
+
module Common
|
11
|
+
extend ::Pwnlib::Shellcraft::Generators::Helper
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: ASCII-8BIT
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'pwnlib/shellcraft/generators/amd64/common/common'
|
5
|
+
require 'pwnlib/shellcraft/generators/x86/common/infloop'
|
6
|
+
|
7
|
+
module Pwnlib
|
8
|
+
module Shellcraft
|
9
|
+
module Generators
|
10
|
+
module Amd64
|
11
|
+
module Common
|
12
|
+
# @overload infloop
|
13
|
+
#
|
14
|
+
# @see Generators::X86::Common#infloop
|
15
|
+
def infloop(*args)
|
16
|
+
context.local(arch: :amd64) do
|
17
|
+
cat X86::Common.infloop(*args)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: ASCII-8BIT
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'pwnlib/shellcraft/generators/amd64/common/common'
|
5
|
+
require 'pwnlib/shellcraft/generators/amd64/common/setregs'
|
6
|
+
|
7
|
+
module Pwnlib
|
8
|
+
module Shellcraft
|
9
|
+
module Generators
|
10
|
+
module Amd64
|
11
|
+
module Common
|
12
|
+
# Like +memcpy+ in glibc.
|
13
|
+
#
|
14
|
+
# Copy +n+ bytes from +src+ to +dst+.
|
15
|
+
#
|
16
|
+
# @param [String, Symbol, Integer] dst
|
17
|
+
# Destination.
|
18
|
+
# @param [String, Symbol, Integer] src
|
19
|
+
# Source to be copied.
|
20
|
+
# @param [Integer] n
|
21
|
+
# The number of bytes to be copied.
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
# shellcraft.memcpy('rax', 'rbx', 0x1000)
|
25
|
+
def memcpy(dst, src, n)
|
26
|
+
cat "/* memcpy(#{pretty(dst)}, #{pretty(src)}, #{pretty(n)}) */"
|
27
|
+
cat 'cld'
|
28
|
+
cat Common.setregs({ rdi: dst, rsi: src, rcx: n })
|
29
|
+
cat 'rep movsb'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# encoding: ASCII-8BIT
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'pwnlib/shellcraft/generators/amd64/common/common'
|
5
|
+
|
6
|
+
module Pwnlib
|
7
|
+
module Shellcraft
|
8
|
+
module Generators
|
9
|
+
module Amd64
|
10
|
+
module Common
|
11
|
+
# Move +src+ into +dst+ without newlines and null bytes.
|
12
|
+
#
|
13
|
+
# @param [String, Symbol] dst
|
14
|
+
# Register's name.
|
15
|
+
# @param [String, Symbol, Integer] src
|
16
|
+
# Register's name or immediate value.
|
17
|
+
# @param [Boolean] stack_allowed
|
18
|
+
# If equals to +false+, generated assembly code would not use stack-related operations.
|
19
|
+
# But beware of without stack-related operations the generated code length is longer.
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# context.arch = 'amd64'
|
23
|
+
# shellcraft.mov('rdi', 'ax')
|
24
|
+
# #=> " movzx edi, ax\n"
|
25
|
+
# @example
|
26
|
+
# context.arch = 'amd64'
|
27
|
+
# puts shellcraft.mov('rax', 10)
|
28
|
+
# # push 9 /* mov eax, '\n' */
|
29
|
+
# # pop rax
|
30
|
+
# # inc eax
|
31
|
+
# #=> nil
|
32
|
+
# @example
|
33
|
+
# context.arch = 'amd64'
|
34
|
+
# puts shellcraft.mov('rax', 10, stack_allowed: false)
|
35
|
+
# # mov eax, 0x1010101
|
36
|
+
# # xor eax, 0x101010b /* 0xa == 0x1010101 ^ 0x101010b */
|
37
|
+
# #=> nil
|
38
|
+
def mov(dst, src, stack_allowed: true)
|
39
|
+
raise ArgumentError, "#{dst} is not a register" unless register?(dst)
|
40
|
+
|
41
|
+
dst = get_register(dst)
|
42
|
+
if register?(src)
|
43
|
+
src = get_register(src)
|
44
|
+
if dst.size < src.size && !dst.bigger.include?(src.name)
|
45
|
+
raise ArgumentError, "cannot mov #{dst}, #{src}: dst is smaller than src"
|
46
|
+
end
|
47
|
+
|
48
|
+
# Downgrade our register choice if possible.
|
49
|
+
# Opcodes for operating on 32-bit registers are always (?) shorter.
|
50
|
+
dst = get_register(dst.native32) if dst.size == 64 && src.size <= 32
|
51
|
+
else
|
52
|
+
context.local(arch: 'amd64') { src = evaluate(src) }
|
53
|
+
raise ArgumentError, format('cannot mov %s, %d: dst is smaller than src', dst, src) unless dst.fits(src)
|
54
|
+
|
55
|
+
orig_dst = dst
|
56
|
+
dst = get_register(dst.native32) if dst.size == 64 && bits_required(src) <= 32
|
57
|
+
|
58
|
+
# Calculate the packed version.
|
59
|
+
srcp = pack(src & ((1 << dst.size) - 1), bits: dst.size)
|
60
|
+
|
61
|
+
# Calculate the unsigned and signed versions.
|
62
|
+
srcu = unpack(srcp, bits: dst.size, signed: false)
|
63
|
+
# N.B.: We may have downsized the register for e.g. mov('rax', 0xffffffff)
|
64
|
+
# In this case, srcp is now a 4-byte packed value, which will expand to "-1", which isn't correct.
|
65
|
+
srcs = orig_dst.size == dst.size ? unpack(srcp, bits: dst.size, signed: true) : src
|
66
|
+
end
|
67
|
+
if register?(src)
|
68
|
+
if src == dst || dst.bigger.include?(src.name)
|
69
|
+
cat "/* moving #{src} into #{dst}, but this is a no-op */"
|
70
|
+
elsif dst.size > src.size
|
71
|
+
cat "movzx #{dst}, #{src}"
|
72
|
+
else
|
73
|
+
cat "mov #{dst}, #{src}"
|
74
|
+
end
|
75
|
+
elsif src.is_a?(Numeric) # Constant or immi
|
76
|
+
xor = ->(reg) { "xor #{reg.xor}, #{reg.xor}" }
|
77
|
+
if src.zero?
|
78
|
+
# Special case for zeroes.
|
79
|
+
# XORing the 32-bit register clears the high 32 bits as well.
|
80
|
+
cat "xor #{dst}, #{dst} /* #{src} */"
|
81
|
+
elsif stack_allowed && [32, 64].include?(dst.size) && src == 10
|
82
|
+
cat "push 9 /* mov #{dst}, '\\n' */"
|
83
|
+
cat "pop #{dst.native64}"
|
84
|
+
cat "inc #{dst}"
|
85
|
+
elsif stack_allowed && [32, 64].include?(dst.size) && (-2**7 <= srcs && srcs < 2**7) && okay(srcp[0])
|
86
|
+
# It's smaller to PUSH and POP small sign-extended values than to directly move them into various
|
87
|
+
# registers.
|
88
|
+
#
|
89
|
+
# 6aff58 push -1; pop rax
|
90
|
+
# 48c7c0ffffffff mov rax, -1
|
91
|
+
cat "push #{pretty(src)}"
|
92
|
+
cat "pop #{dst.native64}"
|
93
|
+
elsif okay(srcp)
|
94
|
+
# Easy case. This implies that the register size and value are the same.
|
95
|
+
cat "mov #{dst}, #{pretty(src)}"
|
96
|
+
elsif srcu < 2**8 && okay(srcp[0]) && dst.sizes.include?(8) # Move 8-bit value into register.
|
97
|
+
cat xor[dst]
|
98
|
+
cat "mov #{dst.sizes[8]}, #{pretty(src)}"
|
99
|
+
elsif srcu == srcu & 0xff00 && okay(srcp[1]) && dst.ff00
|
100
|
+
# Target value is a 16-bit value with no data in the low 8 bits, we can use the 'AH' style register.
|
101
|
+
cat xor[dst]
|
102
|
+
cat "mov #{dst.ff00}, #{pretty(src)} >> 8"
|
103
|
+
elsif srcu < 2**16 && okay(srcp[0, 2]) # Target value is a 16-bit value, use a 16-bit mov.
|
104
|
+
cat xor[dst]
|
105
|
+
cat "mov #{dst.sizes[16]}, #{pretty(src)}"
|
106
|
+
else # All else has failed. Use some XOR magic to move things around.
|
107
|
+
a, b = xor_pair(srcp, avoid: "\x00\n")
|
108
|
+
a = hex(unpack(a, bits: dst.size))
|
109
|
+
b = hex(unpack(b, bits: dst.size))
|
110
|
+
if dst.size != 64
|
111
|
+
# There's no XOR REG, IMM64 but we can take the easy route for smaller registers.
|
112
|
+
cat "mov #{dst}, #{a}"
|
113
|
+
cat "xor #{dst}, #{b} /* #{hex(src)} == #{a} ^ #{b} */"
|
114
|
+
elsif stack_allowed
|
115
|
+
# However, we can PUSH IMM64 and then perform the XOR that way at the top of the stack.
|
116
|
+
cat "mov #{dst}, #{a}"
|
117
|
+
cat "push #{dst}"
|
118
|
+
cat "mov #{dst}, #{b}"
|
119
|
+
cat "xor [rsp], #{dst} /* #{hex(src)} == #{a} ^ #{b} */"
|
120
|
+
cat "pop #{dst}"
|
121
|
+
else
|
122
|
+
raise ArgumentError, "Cannot put #{pretty(src)} into '#{dst}' without using stack."
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|