pwntools 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,26 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ # require this file would also require all things in pwnlib, but would not
4
+ # pollute anything.
5
+
6
+ require 'pwnlib/constants/constant'
7
+ require 'pwnlib/constants/constants'
8
+ require 'pwnlib/context'
9
+ require 'pwnlib/dynelf'
10
+ require 'pwnlib/reg_sort'
11
+
12
+ require 'pwnlib/util/cyclic'
13
+ require 'pwnlib/util/fiddling'
14
+ require 'pwnlib/util/hexdump'
15
+ require 'pwnlib/util/packing'
16
+
17
+ # include this module in a class to use all pwnlib functions in that class
18
+ # instance.
19
+ module Pwn
20
+ include ::Pwnlib::Context
21
+
22
+ include ::Pwnlib::Util::Cyclic::ClassMethods
23
+ include ::Pwnlib::Util::Fiddling::ClassMethods
24
+ include ::Pwnlib::Util::HexDump::ClassMethods
25
+ include ::Pwnlib::Util::Packing::ClassMethods
26
+ end
@@ -0,0 +1,147 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ require 'pwnlib/context'
4
+
5
+ module Pwnlib
6
+ # Do topological sort on register assignments.
7
+ module RegSort
8
+ # @note Do not create and call instance method here. Instead, call module method on {RegSort}.
9
+ module ClassMethods
10
+ # Sorts register dependencies.
11
+ #
12
+ # Given a dictionary of registers to desired register contents,
13
+ # return the optimal order in which to set the registers to
14
+ # those contents.
15
+ #
16
+ # The implementation assumes that it is possible to move from
17
+ # 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
25
+ # regular values.
26
+ # @option [Boolean] randomize
27
+ # Randomize as much as possible about the order or registers.
28
+ #
29
+ # @return [Array]
30
+ # Array of instructions, see examples for more details.
31
+ #
32
+ # @example
33
+ # regs = %w(a b c d x y z)
34
+ # regsort({a: 1, b: 2}, regs)
35
+ # => [['mov', 'a', 1], ['mov', 'b', 2]]
36
+ # regsort({a: 'b', b: 'a'}, regs)
37
+ # => [['xchg', 'a', 'b']]
38
+ # regsort({a: 1, b: 'a'}, regs)
39
+ # => [['mov', 'b', 'a'], ['mov', 'a', 1]]
40
+ # regsort({a: 'b', b: 'a', c: 3}, regs)
41
+ # => [['mov', 'c', 3], ['xchg', 'a', 'b']]
42
+ # regsort({a: 'b', b: 'a', c: 'b'}, regs)
43
+ # => [['mov', 'c', 'b'], ['xchg', 'a', 'b']]
44
+ # regsort({a: 'b', b: 'c', c: 'a', x: '1', y: 'z', z: 'c'}, regs)
45
+ # => [['mov', 'x', '1'],
46
+ # ['mov', 'y', 'z'],
47
+ # ['mov', 'z', 'c'],
48
+ # ['xchg', 'a', 'b'],
49
+ # ['xchg', 'b', 'c']]
50
+ #
51
+ # @note
52
+ # Different from python-pwntools, we don't support +tmp+/+xchg+ options
53
+ # because there's no such usage at all.
54
+ def regsort(in_out, all_regs, randomize: nil)
55
+ # randomize = context.randomize if randomize.nil?
56
+
57
+ # TODO(david942j): stringify_keys
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
+
68
+ # Collapse constant values
69
+ #
70
+ # Ex. {eax: 1, ebx: 1} can be collapsed to {eax: 1, ebx: 'eax'}.
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
81
+ end
82
+
83
+ graph = in_out.dup
84
+ result = []
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
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]
109
+ end
110
+ cycle.each { |r| graph.delete(r) }
111
+ end
112
+
113
+ # Now assign those collapsed registers.
114
+ post_mov.sort.each do |dreg, sreg|
115
+ result << ['mov', dreg, sreg]
116
+ end
117
+
118
+ result
119
+ end
120
+
121
+ private
122
+
123
+ # Walk down the assignment list of a register,
124
+ # return the path walked if it is encountered again.
125
+ # @example
126
+ # check_cycle('a', {'a' => 1}) #=> []
127
+ # check_cycle('a', {'a' => 'a'}) #=> ['a']
128
+ # check_cycle('a', {'a' => 'b', 'b' => 'c', 'c' => 'b', 'd' => 'a'}) #=> []
129
+ # check_cycle('a', {'a' => 'b', 'b' => 'c', 'c' => 'd', 'd' => 'a'})
130
+ # #=> ['a', 'b', 'c', 'd']
131
+ def check_cycle(reg, assignments)
132
+ check_cycle_(reg, assignments, [])
133
+ end
134
+
135
+ def check_cycle_(reg, assignments, path) # :nodoc:
136
+ target = assignments[reg]
137
+ path << reg
138
+ # No cycle, some other value (e.g. 1)
139
+ return [] unless assignments.key?(target)
140
+ # Found a cycle
141
+ return target == path.first ? path : [] if path.include?(target)
142
+ check_cycle_(target, assignments, path)
143
+ end
144
+ end
145
+ extend ClassMethods
146
+ end
147
+ end
@@ -0,0 +1,120 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ module Pwnlib
4
+ module Util
5
+ # Generate string with easy-to-find pattern.
6
+ # See {ClassMethods} for method details.
7
+ # @example Call by specifying full module path.
8
+ # require 'pwnlib/util/cyclic'
9
+ # Pwnlib::Util::Cyclic.cyclic_find(Pwnlib::Util::Cyclic.cyclic(200)[123, 4]) #=> 123
10
+ # @example require 'pwn' and have all methods.
11
+ # require 'pwn'
12
+ # cyclic_find(cyclic(200)[123, 4]) #=> 123
13
+ module Cyclic
14
+ # @note Do not create and call instance method here. Instead, call module method on {Cyclic}.
15
+ module ClassMethods
16
+ # TODO(Darkpi): Should we put this constant in some 'String' module?
17
+ ASCII_LOWERCASE = ('a'..'z').to_a.join
18
+ private_constant :ASCII_LOWERCASE
19
+
20
+ # Generator for a sequence of unique substrings of length +n+.
21
+ # This is implemented using a De Bruijn Sequence over the given +alphabet+.
22
+ # Returns an Enumerator if no block given.
23
+ #
24
+ # @overload de_bruijn(alphabet: ASCII_LOWERCASE, n: 4)
25
+ # @param [String, Array] alphabet
26
+ # Alphabet to be used.
27
+ # @param [Integer] n
28
+ # Length of substring that should be unique.
29
+ # @return [void]
30
+ # @yieldparam c
31
+ # Item of the result sequence in order.
32
+ # @overload de_bruijn(alphabet: ASCII_LOWERCASE, n: 4)
33
+ # @param [String, Array] alphabet
34
+ # Alphabet to be used.
35
+ # @param [Integer] n
36
+ # Length of substring that should be unique.
37
+ # @return [Enumerator]
38
+ # The result sequence.
39
+ def de_bruijn(alphabet: ASCII_LOWERCASE, n: 4)
40
+ return to_enum(__method__, alphabet: alphabet, n: n) { alphabet.size**n } unless block_given?
41
+ k = alphabet.size
42
+ a = [0] * (k * n)
43
+
44
+ db = lambda do |t, p|
45
+ if t > n
46
+ (1..p).each { |j| yield alphabet[a[j]] } if (n % p).zero?
47
+ else
48
+ a[t] = a[t - p]
49
+ db.call(t + 1, p)
50
+ (a[t - p] + 1...k).each do |j|
51
+ a[t] = j
52
+ db.call(t + 1, t)
53
+ end
54
+ end
55
+ end
56
+
57
+ db[1, 1]
58
+ end
59
+
60
+ # Simple wrapper over {#de_bruijn}, returning at most +length+ items.
61
+ #
62
+ # @param [Integer, nil] length
63
+ # Desired length of the sequence,
64
+ # or +nil+ for the entire sequence.
65
+ # @param [String, Array] alphabet
66
+ # Alphabet to be used.
67
+ # @param [Integer] n
68
+ # Length of substring that should be unique.
69
+ # @return [String, Array]
70
+ # The result sequence of at most +length+ items,
71
+ # with same type as +alphabet+.
72
+ #
73
+ # @example
74
+ # cyclic(alphabet: 'ABC', n: 3) #=> 'AAABAACABBABCACBACCBBBCBCCC'
75
+ # cyclic(20) #=> 'aaaabaaacaaadaaaeaaa'
76
+ def cyclic(length = nil, alphabet: ASCII_LOWERCASE, n: 4)
77
+ enum = de_bruijn(alphabet: alphabet, n: n)
78
+ r = length.nil? ? enum.to_a : enum.take(length)
79
+ alphabet.is_a?(String) ? r.join : r
80
+ end
81
+
82
+ # Find the position of a substring in a De Bruijn sequence
83
+ #
84
+ # @todo Speed! See comment in Python pwntools.
85
+ # @param [String, Array] subseq
86
+ # The substring to be found in the sequence.
87
+ # @param [String, Array] alphabet
88
+ # Alphabet to be used.
89
+ # @param [Integer] n
90
+ # Length of substring that should be unique.
91
+ # Default to +subseq.size+.
92
+ # @return [Integer, nil]
93
+ # The index +subseq+ first appear in the sequence,
94
+ # or +nil+ if not found.
95
+ #
96
+ # @example
97
+ # cyclic_find(cyclic(300)[217, 4]) #=> 217
98
+ def cyclic_find(subseq, alphabet: ASCII_LOWERCASE, n: nil)
99
+ n ||= subseq.size
100
+ subseq = subseq.chars if subseq.is_a?(String)
101
+ return nil unless subseq.all? { |c| alphabet.include?(c) }
102
+
103
+ pos = 0
104
+ saved = []
105
+ de_bruijn(alphabet: alphabet, n: n).each do |c|
106
+ saved << c
107
+ if saved.size > subseq.size
108
+ saved.shift
109
+ pos += 1
110
+ end
111
+ return pos if saved == subseq
112
+ end
113
+ nil
114
+ end
115
+ end
116
+
117
+ extend ClassMethods
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,262 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ require 'pwnlib/context'
4
+
5
+ module Pwnlib
6
+ module Util
7
+ # Some fiddling methods.
8
+ # See {ClassMethods} for method details.
9
+ # @example Call by specifying full module path.
10
+ # require 'pwnlib/util/fiddling'
11
+ # Pwnlib::Util::Fiddling.enhex('217') #=> '323137'
12
+ # @example require 'pwn' and have all methods.
13
+ # require 'pwn'
14
+ # enhex('217') #=> '323137'
15
+ module Fiddling
16
+ # @note Do not create and call instance method here. Instead, call module method on {Fiddling}.
17
+ module ClassMethods
18
+ # Hex-encodes a string.
19
+ #
20
+ # @param [String] s
21
+ # String to be encoded.
22
+ # @return [String]
23
+ # Hex-encoded string.
24
+ #
25
+ # @example
26
+ # enhex('217') #=> '323137'
27
+ def enhex(s)
28
+ s.unpack('H*')[0]
29
+ end
30
+
31
+ # Hex-decodes a string.
32
+ #
33
+ # @param [String] s
34
+ # String to be decoded.
35
+ # @return [String]
36
+ # Hex-decoded string.
37
+ #
38
+ # @example
39
+ # unhex('353134') #=> '514'
40
+ def unhex(s)
41
+ [s].pack('H*')
42
+ end
43
+
44
+ # Present number in hex format, same as python hex() do.
45
+ #
46
+ # @param [Integer] n
47
+ # The number.
48
+ #
49
+ # @return [String]
50
+ # The hex format string.
51
+ #
52
+ # @example
53
+ # hex(0) #=> '0x0'
54
+ # hex(-10) #=> '-0xa'
55
+ # hex(0xfaceb00cdeadbeef) #=> '0xfaceb00cdeadbeef'
56
+ def hex(n)
57
+ (n < 0 ? '-' : '') + format('0x%x', n.abs)
58
+ end
59
+
60
+ # URL-encodes a string.
61
+ #
62
+ # @param [String] s
63
+ # String to be encoded.
64
+ # @return [String]
65
+ # URL-encoded string.
66
+ #
67
+ # @example
68
+ # urlencode('shikway') #=> '%73%68%69%6b%77%61%79'
69
+ def urlencode(s)
70
+ s.bytes.map { |b| format('%%%02x', b) }.join
71
+ end
72
+
73
+ # URL-decodes a string.
74
+ #
75
+ # @param [String] s
76
+ # String to be decoded.
77
+ # @param [Boolean] ignore_invalid
78
+ # Whether invalid encoding should be ignore.
79
+ # If set to +true+,
80
+ # invalid encoding in input are left intact to output.
81
+ # @return [String]
82
+ # URL-decoded string.
83
+ # @raise [ArgumentError]
84
+ # If +ignore_invalid+ is +false+,
85
+ # and there are invalid encoding in input.
86
+ #
87
+ # @example
88
+ # urldecode('test%20url') #=> 'test url'
89
+ # urldecode('%qw%er%ty') #=> raise ArgumentError
90
+ # urldecode('%qw%er%ty', true) #=> '%qw%er%ty'
91
+ def urldecode(s, ignore_invalid = false)
92
+ res = ''
93
+ n = 0
94
+ while n < s.size
95
+ if s[n] != '%'
96
+ res << s[n]
97
+ n += 1
98
+ else
99
+ cur = s[n + 1, 2]
100
+ if cur =~ /[0-9a-fA-F]{2}/
101
+ res << cur.to_i(16).chr
102
+ n += 3
103
+ elsif ignore_invalid
104
+ res << '%'
105
+ n += 1
106
+ else
107
+ raise ArgumentError, 'Invalid input to urldecode'
108
+ end
109
+ end
110
+ end
111
+ res
112
+ end
113
+
114
+ # Converts the argument to an array of bits.
115
+ #
116
+ # @param [String, Integer] s
117
+ # Input to be converted into bits.
118
+ # If input is integer,
119
+ # output would be padded to byte aligned.
120
+ # @param [String] endian
121
+ # Endian for conversion.
122
+ # Can be any value accepted by context (See {Context::ContextType}).
123
+ # @param zero
124
+ # Object representing a 0-bit.
125
+ # @param one
126
+ # Object representing a 1-bit.
127
+ # @return [Array]
128
+ # An array consisting of +zero+ and +one+.
129
+ #
130
+ # @example
131
+ # bits(314) #=> [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0]
132
+ # bits('orz', zero: '-', one: '+').join #=> '-++-++++-+++--+--++++-+-'
133
+ # bits(128, endian: 'little') #=> [0, 0, 0, 0, 0, 0, 0, 1]
134
+ def bits(s, endian: 'big', zero: 0, one: 1)
135
+ context.local(endian: endian) do
136
+ is_little = context.endian == 'little'
137
+ case s
138
+ when String
139
+ v = 'B*'
140
+ v.downcase! if is_little
141
+ s.unpack(v)[0].chars.map { |ch| ch == '1' ? one : zero }
142
+ when Integer
143
+ # TODO(Darkpi): What should we do to negative number?
144
+ raise ArgumentError, 's must be non-negative' unless s >= 0
145
+ r = s.to_s(2).chars.map { |ch| ch == '1' ? one : zero }
146
+ r.unshift(zero) until (r.size % 8).zero?
147
+ is_little ? r.reverse : r
148
+ else
149
+ raise ArgumentError, 's must be either String or Integer'
150
+ end
151
+ end
152
+ end
153
+
154
+ # Simple wrapper around {#bits}, which converts output to string.
155
+ #
156
+ # @param (see #bits)
157
+ # @return [String]
158
+ #
159
+ # @example
160
+ # bits_str('GG') #=> '0100011101000111'
161
+ def bits_str(s, endian: 'big', zero: 0, one: 1)
162
+ bits(s, endian: endian, zero: zero, one: one).join
163
+ end
164
+
165
+ # Reverse of {#bits} and {#bits_str}, convert an array of bits back to string.
166
+ #
167
+ # @param [String, Array<String, Integer, Boolean>] s
168
+ # String or array of bits to be convert back to string.
169
+ # <tt>[0, '0', false]</tt> represents 0-bit,
170
+ # and <tt>[1, '1', true]</tt> represents 1-bit.
171
+ # @param [String] endian
172
+ # Endian for conversion.
173
+ # Can be any value accepted by context (See {Context::ContextType}).
174
+ # @raise [ArgumentError]
175
+ # If input contains value not in <tt>[0, 1, '0', '1', true, false]</tt>.
176
+ #
177
+ # @example
178
+ # unbits('0100011101000111') #=> 'GG'
179
+ # unbits([0, 1, 0, 1, 0, 1, 0, 0]) #=> 'T'
180
+ # unbits('0100011101000111', endian: 'little') #=> "\xE2\xE2"
181
+ def unbits(s, endian: 'big')
182
+ s = s.chars if s.is_a?(String)
183
+ context.local(endian: endian) do
184
+ is_little = context.endian == 'little'
185
+ bytes = s.map do |c|
186
+ case c
187
+ when '1', 1, true then '1'
188
+ when '0', 0, false then '0'
189
+ else raise ArgumentError, "cannot decode value #{c.inspect} into a bit"
190
+ end
191
+ end
192
+ [bytes.join].pack(is_little ? 'b*' : 'B*')
193
+ end
194
+ end
195
+
196
+ # Reverse the bits of each byte in input string.
197
+ #
198
+ # @param [String] s
199
+ # Input string.
200
+ # @return [String]
201
+ # The string with bits of each byte reversed.
202
+ #
203
+ # @example
204
+ # bitswap('rb') #=> 'NF'
205
+ def bitswap(s)
206
+ unbits(bits(s, endian: 'big'), endian: 'little')
207
+ end
208
+
209
+ # Reverse the bits of a number, and returns the result as number.
210
+ #
211
+ # @param [Integer] n
212
+ # @param [Integer] bits
213
+ # The bit length of +n+,
214
+ # only the lower +bits+ bits of +n+ would be used.
215
+ # Default to context.bits
216
+ # @return [Integer]
217
+ # The number with bits reversed.
218
+ #
219
+ # @example
220
+ # bitswap_int(217, bits: 8) #=> 155
221
+ def bitswap_int(n, bits: nil)
222
+ context.local(bits: bits) do
223
+ bits = context.bits
224
+ n &= (1 << bits) - 1
225
+ bits_str(n, endian: 'little').ljust(bits, '0').to_i(2)
226
+ end
227
+ end
228
+
229
+ # Base64-encodes a string.
230
+ # Do NOT contains those stupid newline (with RFC 4648)
231
+ #
232
+ # @param [String] s
233
+ # String to be encoded.
234
+ # @return [String]
235
+ # Base64-encoded string.
236
+ #
237
+ # @example
238
+ # b64e('desu') #=> 'ZGVzdQ=='
239
+ def b64e(s)
240
+ [s].pack('m0')
241
+ end
242
+
243
+ # Base64-decodes a string.
244
+ #
245
+ # @param [String] s
246
+ # String to be decoded.
247
+ # @return [String]
248
+ # Base64-decoded string.
249
+ #
250
+ # @example
251
+ # b64d('ZGVzdQ==') #=> 'desu'
252
+ def b64d(s)
253
+ s.unpack('m0')[0]
254
+ end
255
+
256
+ include ::Pwnlib::Context
257
+ end
258
+
259
+ extend ClassMethods
260
+ end
261
+ end
262
+ end