Module: Pwnlib::Util::Fiddling

Included in:
Pwn, Shellcraft::Generators::Helper::Runner
Defined in:
lib/pwnlib/util/fiddling.rb

Overview

Some fiddling methods.

Examples:

Call by specifying full module path.

require 'pwnlib/util/fiddling'
Pwnlib::Util::Fiddling.enhex('217') #=> '323137'

require 'pwn' and have all methods.

require 'pwn'
enhex('217') #=> '323137'

Class Method Summary collapse

Class Method Details

.b64d(s) ⇒ String

Base64-decodes a string.

Examples:

b64d('ZGVzdQ==') #=> 'desu'


266
267
268
# File 'lib/pwnlib/util/fiddling.rb', line 266

def b64d(s)
  s.unpack1('m0')
end

.b64e(s) ⇒ String

Base64-encodes a string. Do NOT contains those stupid newline (with RFC 4648).

Examples:

b64e('desu') #=> 'ZGVzdQ=='


252
253
254
# File 'lib/pwnlib/util/fiddling.rb', line 252

def b64e(s)
  [s].pack('m0')
end

.bits(s, endian: 'big', zero: 0, one: 1) ⇒ Array

Converts the argument to an array of bits.

Examples:

bits(314) #=> [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0]
bits('orz', zero: '-', one: '+').join #=> '-++-++++-+++--+--++++-+-'
bits(128, endian: 'little') #=> [0, 0, 0, 0, 0, 0, 0, 1]


138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/pwnlib/util/fiddling.rb', line 138

def bits(s, endian: 'big', zero: 0, one: 1)
  context.local(endian: endian) do
    is_little = context.endian == 'little'
    case s
    when String
      v = +'B*'
      v.downcase! if is_little
      s.unpack1(v).chars.map { |ch| ch == '1' ? one : zero }
    when Integer
      # TODO(Darkpi): What should we do to negative number?
      raise ArgumentError, 's must be non-negative' unless s >= 0

      r = s.to_s(2).chars.map { |ch| ch == '1' ? one : zero }
      r.unshift(zero) until (r.size % 8).zero?
      is_little ? r.reverse : r
    else
      raise ArgumentError, 's must be either String or Integer'
    end
  end
end

.bits_str(s, endian: 'big', zero: 0, one: 1) ⇒ String

Simple wrapper around bits, which converts output to string.

Examples:

bits_str('GG') #=> '0100011101000111'


168
169
170
# File 'lib/pwnlib/util/fiddling.rb', line 168

def bits_str(s, endian: 'big', zero: 0, one: 1)
  bits(s, endian: endian, zero: zero, one: one).join
end

.bitswap(s) ⇒ String

Reverse the bits of each byte in input string.

Examples:

bitswap('rb') #=> 'NF'


216
217
218
# File 'lib/pwnlib/util/fiddling.rb', line 216

def bitswap(s)
  unbits(bits(s, endian: 'big'), endian: 'little')
end

.bitswap_int(n, bits: nil) ⇒ Integer

Reverse the bits of a number, and returns the result as number.

Examples:

bitswap_int(217, bits: 8) #=> 155


233
234
235
236
237
238
239
# File 'lib/pwnlib/util/fiddling.rb', line 233

def bitswap_int(n, bits: nil)
  context.local(bits: bits) do
    bits = context.bits
    n &= (1 << bits) - 1
    bits_str(n, endian: 'little').ljust(bits, '0').to_i(2)
  end
end

.enhex(s) ⇒ String

Hex-encodes a string.

Examples:

enhex('217') #=> '323137'


29
30
31
# File 'lib/pwnlib/util/fiddling.rb', line 29

def enhex(s)
  s.unpack1('H*')
end

.hex(n) ⇒ String

Present number in hex format, same as python hex() do.

Examples:

hex(0) #=> '0x0'
hex(-10) #=> '-0xa'
hex(0xfaceb00cdeadbeef) #=> '0xfaceb00cdeadbeef'


59
60
61
# File 'lib/pwnlib/util/fiddling.rb', line 59

def hex(n)
  (n.negative? ? '-' : '') + format('0x%x', n.abs)
end

.unbits(s, endian: 'big') ⇒ String

Reverse of bits and bits_str, convert an array of bits back to string.

Examples:

unbits('0100011101000111') #=> 'GG'
unbits([0, 1, 0, 1, 0, 1, 0, 0]) #=> 'T'
unbits('0100011101000111', endian: 'little') #=> "\xE2\xE2"

Raises:

  • (ArgumentError)

    If input contains value not in [0, 1, '0', '1', true, false].



191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/pwnlib/util/fiddling.rb', line 191

def unbits(s, endian: 'big')
  s = s.chars if s.is_a?(String)
  context.local(endian: endian) do
    is_little = context.endian == 'little'
    bytes = s.map do |c|
      case c
      when '1', 1, true then '1'
      when '0', 0, false then '0'
      else raise ArgumentError, "cannot decode value #{c.inspect} into a bit"
      end
    end
    [bytes.join].pack(is_little ? 'b*' : 'B*')
  end
end

.unhex(s) ⇒ String

Hex-decodes a string.

Examples:

unhex('353134') #=> '514'


43
44
45
# File 'lib/pwnlib/util/fiddling.rb', line 43

def unhex(s)
  [s].pack('H*')
end

.urldecode(s, ignore_invalid: false) ⇒ String

URL-decodes a string.

Examples:

urldecode('test%20url') #=> 'test url'
urldecode('%qw%er%ty') #=> raise ArgumentError
urldecode('%qw%er%ty', ignore_invalid: true) #=> '%qw%er%ty'

Raises:

  • (ArgumentError)

    If ignore_invalid is false, and there are invalid encoding in input.



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/pwnlib/util/fiddling.rb', line 95

def urldecode(s, ignore_invalid: false)
  res = +''
  n = 0
  while n < s.size
    if s[n] != '%'
      res << s[n]
      n += 1
    else
      cur = s[n + 1, 2]
      if cur =~ /[0-9a-fA-F]{2}/
        res << cur.to_i(16).chr
        n += 3
      elsif ignore_invalid
        res << '%'
        n += 1
      else
        raise ArgumentError, 'Invalid input to urldecode'
      end
    end
  end
  res
end

.urlencode(s) ⇒ String

URL-encodes a string.

Examples:

urlencode('shikway') #=> '%73%68%69%6b%77%61%79'


73
74
75
# File 'lib/pwnlib/util/fiddling.rb', line 73

def urlencode(s)
  s.bytes.map { |b| format('%%%02x', b) }.join
end

.xor(s1, s2) ⇒ String

Xor two strings. If two strings have different length, the shorter one will be repeated until has the same length as another one.

Examples:

xor("\xE8\xE1\xF0\xF0\xF9", "\x80")
=> 'happy'

xor("\x80", "\xE8\xE1\xF0\xF0\xF9")
=> 'happy'

xor('plaintext', 'thekey')
=> "\x04\x04\x04\x02\v\r\x11\x10\x11"

xor('217', "\x00" * 10)
=> '2172172172'


294
295
296
297
# File 'lib/pwnlib/util/fiddling.rb', line 294

def xor(s1, s2)
  s1, s2 = s2, s1 if s1.size < s2.size
  s1.bytes.zip(''.ljust(s1.size, s2).bytes).map { |a, b| a ^ b }.pack('C*')
end

.xor_pair(data, avoid: "\x00\n") ⇒ (String, String)?

Find two strings that will xor into a given string, while only using a given alphabet.

Examples:

xor_pair("test") #=> ["\x01\x01\x01\x01", 'udru']


311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
# File 'lib/pwnlib/util/fiddling.rb', line 311

def xor_pair(data, avoid: "\x00\n")
  data = pack(data) if data.is_a?(Integer)
  alphabet = 256.times.reject { |c| avoid.include?(c.chr) }
  res1 = +''
  res2 = +''
  data.bytes.each do |c1|
    # alphabet.shuffle! if context.randomize
    c2 = alphabet.find { |c| alphabet.include?(c1 ^ c) }
    return nil if c2.nil?

    res1 << c2.chr
    res2 << (c1 ^ c2).chr
  end
  [res1, res2]
end