pwntools 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +88 -11
  3. data/Rakefile +5 -1
  4. data/lib/pwn.rb +9 -7
  5. data/lib/pwnlib/abi.rb +60 -0
  6. data/lib/pwnlib/asm.rb +146 -0
  7. data/lib/pwnlib/constants/constant.rb +16 -2
  8. data/lib/pwnlib/constants/constants.rb +35 -19
  9. data/lib/pwnlib/constants/linux/amd64.rb +30 -1
  10. data/lib/pwnlib/context.rb +25 -17
  11. data/lib/pwnlib/dynelf.rb +117 -54
  12. data/lib/pwnlib/elf/elf.rb +267 -0
  13. data/lib/pwnlib/ext/helper.rb +4 -4
  14. data/lib/pwnlib/logger.rb +87 -0
  15. data/lib/pwnlib/memleak.rb +58 -29
  16. data/lib/pwnlib/pwn.rb +19 -8
  17. data/lib/pwnlib/reg_sort.rb +102 -108
  18. data/lib/pwnlib/shellcraft/generators/amd64/common/common.rb +14 -0
  19. data/lib/pwnlib/shellcraft/generators/amd64/common/infloop.rb +17 -0
  20. data/lib/pwnlib/shellcraft/generators/amd64/common/memcpy.rb +31 -0
  21. data/lib/pwnlib/shellcraft/generators/amd64/common/mov.rb +127 -0
  22. data/lib/pwnlib/shellcraft/generators/amd64/common/nop.rb +16 -0
  23. data/lib/pwnlib/shellcraft/generators/amd64/common/popad.rb +27 -0
  24. data/lib/pwnlib/shellcraft/generators/amd64/common/pushstr.rb +64 -0
  25. data/lib/pwnlib/shellcraft/generators/amd64/common/pushstr_array.rb +19 -0
  26. data/lib/pwnlib/shellcraft/generators/amd64/common/ret.rb +32 -0
  27. data/lib/pwnlib/shellcraft/generators/amd64/common/setregs.rb +19 -0
  28. data/lib/pwnlib/shellcraft/generators/amd64/linux/execve.rb +21 -0
  29. data/lib/pwnlib/shellcraft/generators/amd64/linux/linux.rb +14 -0
  30. data/lib/pwnlib/shellcraft/generators/amd64/linux/ls.rb +19 -0
  31. data/lib/pwnlib/shellcraft/generators/amd64/linux/sh.rb +19 -0
  32. data/lib/pwnlib/shellcraft/generators/amd64/linux/syscall.rb +21 -0
  33. data/lib/pwnlib/shellcraft/generators/helper.rb +106 -0
  34. data/lib/pwnlib/shellcraft/generators/i386/common/common.rb +14 -0
  35. data/lib/pwnlib/shellcraft/generators/i386/common/infloop.rb +17 -0
  36. data/lib/pwnlib/shellcraft/generators/i386/common/mov.rb +90 -0
  37. data/lib/pwnlib/shellcraft/generators/i386/common/nop.rb +16 -0
  38. data/lib/pwnlib/shellcraft/generators/i386/common/pushstr.rb +39 -0
  39. data/lib/pwnlib/shellcraft/generators/i386/common/pushstr_array.rb +19 -0
  40. data/lib/pwnlib/shellcraft/generators/i386/common/setregs.rb +19 -0
  41. data/lib/pwnlib/shellcraft/generators/i386/linux/execve.rb +19 -0
  42. data/lib/pwnlib/shellcraft/generators/i386/linux/linux.rb +14 -0
  43. data/lib/pwnlib/shellcraft/generators/i386/linux/ls.rb +19 -0
  44. data/lib/pwnlib/shellcraft/generators/i386/linux/sh.rb +19 -0
  45. data/lib/pwnlib/shellcraft/generators/i386/linux/syscall.rb +19 -0
  46. data/lib/pwnlib/shellcraft/generators/x86/common/common.rb +26 -0
  47. data/lib/pwnlib/shellcraft/generators/x86/common/infloop.rb +22 -0
  48. data/lib/pwnlib/shellcraft/generators/x86/common/mov.rb +15 -0
  49. data/lib/pwnlib/shellcraft/generators/x86/common/pushstr.rb +15 -0
  50. data/lib/pwnlib/shellcraft/generators/x86/common/pushstr_array.rb +85 -0
  51. data/lib/pwnlib/shellcraft/generators/x86/common/setregs.rb +82 -0
  52. data/lib/pwnlib/shellcraft/generators/x86/linux/execve.rb +69 -0
  53. data/lib/pwnlib/shellcraft/generators/x86/linux/linux.rb +14 -0
  54. data/lib/pwnlib/shellcraft/generators/x86/linux/ls.rb +66 -0
  55. data/lib/pwnlib/shellcraft/generators/x86/linux/sh.rb +52 -0
  56. data/lib/pwnlib/shellcraft/generators/x86/linux/syscall.rb +52 -0
  57. data/lib/pwnlib/shellcraft/registers.rb +145 -0
  58. data/lib/pwnlib/shellcraft/shellcraft.rb +67 -0
  59. data/lib/pwnlib/timer.rb +60 -0
  60. data/lib/pwnlib/tubes/buffer.rb +96 -0
  61. data/lib/pwnlib/tubes/sock.rb +95 -0
  62. data/lib/pwnlib/tubes/tube.rb +270 -0
  63. data/lib/pwnlib/util/cyclic.rb +95 -94
  64. data/lib/pwnlib/util/fiddling.rb +256 -220
  65. data/lib/pwnlib/util/getdents.rb +83 -0
  66. data/lib/pwnlib/util/hexdump.rb +109 -108
  67. data/lib/pwnlib/util/lists.rb +55 -0
  68. data/lib/pwnlib/util/packing.rb +226 -228
  69. data/lib/pwnlib/util/ruby.rb +18 -0
  70. data/lib/pwnlib/version.rb +2 -1
  71. data/test/abi_test.rb +21 -0
  72. data/test/asm_test.rb +104 -0
  73. data/test/constants/constant_test.rb +1 -0
  74. data/test/constants/constants_test.rb +4 -2
  75. data/test/context_test.rb +1 -0
  76. data/test/data/echo.rb +20 -0
  77. data/test/data/elfs/Makefile +22 -0
  78. data/test/data/elfs/amd64.frelro.elf +0 -0
  79. data/test/data/elfs/amd64.frelro.pie.elf +0 -0
  80. data/test/data/elfs/amd64.nrelro.elf +0 -0
  81. data/test/data/elfs/amd64.prelro.elf +0 -0
  82. data/test/data/elfs/i386.frelro.pie.elf +0 -0
  83. data/test/data/elfs/i386.prelro.elf +0 -0
  84. data/test/data/elfs/source.cpp +19 -0
  85. data/test/data/flag +1 -0
  86. data/test/data/lib32/ld.so.2 +0 -0
  87. data/test/data/lib32/libc.so.6 +0 -0
  88. data/test/data/lib64/ld.so.2 +0 -0
  89. data/test/data/lib64/libc.so.6 +0 -0
  90. data/test/dynelf_test.rb +59 -24
  91. data/test/elf/elf_test.rb +120 -0
  92. data/test/ext_test.rb +3 -2
  93. data/test/files/use_pwnlib.rb +1 -1
  94. data/test/logger_test.rb +61 -0
  95. data/test/memleak_test.rb +4 -33
  96. data/test/reg_sort_test.rb +3 -1
  97. data/test/shellcraft/infloop_test.rb +26 -0
  98. data/test/shellcraft/linux/ls_test.rb +108 -0
  99. data/test/shellcraft/linux/sh_test.rb +119 -0
  100. data/test/shellcraft/linux/syscalls/execve_test.rb +136 -0
  101. data/test/shellcraft/linux/syscalls/syscall_test.rb +83 -0
  102. data/test/shellcraft/memcpy_test.rb +35 -0
  103. data/test/shellcraft/mov_test.rb +98 -0
  104. data/test/shellcraft/nop_test.rb +26 -0
  105. data/test/shellcraft/popad_test.rb +29 -0
  106. data/test/shellcraft/pushstr_array_test.rb +91 -0
  107. data/test/shellcraft/pushstr_test.rb +108 -0
  108. data/test/shellcraft/registers_test.rb +32 -0
  109. data/test/shellcraft/ret_test.rb +30 -0
  110. data/test/shellcraft/setregs_test.rb +62 -0
  111. data/test/shellcraft/shellcraft_test.rb +28 -0
  112. data/test/test_helper.rb +12 -1
  113. data/test/timer_test.rb +23 -0
  114. data/test/tubes/buffer_test.rb +45 -0
  115. data/test/tubes/sock_test.rb +68 -0
  116. data/test/tubes/tube_test.rb +241 -0
  117. data/test/util/cyclic_test.rb +2 -1
  118. data/test/util/fiddling_test.rb +2 -1
  119. data/test/util/getdents_test.rb +32 -0
  120. data/test/util/hexdump_test.rb +7 -9
  121. data/test/util/lists_test.rb +21 -0
  122. data/test/util/packing_test.rb +4 -3
  123. metadata +215 -25
@@ -5,7 +5,7 @@ require 'pwnlib/context'
5
5
  module Pwnlib
6
6
  module Util
7
7
  # Some fiddling methods.
8
- # See {ClassMethods} for method details.
8
+ #
9
9
  # @example Call by specifying full module path.
10
10
  # require 'pwnlib/util/fiddling'
11
11
  # Pwnlib::Util::Fiddling.enhex('217') #=> '323137'
@@ -13,250 +13,286 @@ module Pwnlib
13
13
  # require 'pwn'
14
14
  # enhex('217') #=> '323137'
15
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
16
+ module_function
30
17
 
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
18
+ # Hex-encodes a string.
19
+ #
20
+ # @param [String] s
21
+ # String to be encoded.
22
+ #
23
+ # @return [String]
24
+ # Hex-encoded string.
25
+ #
26
+ # @example
27
+ # enhex('217') #=> '323137'
28
+ def enhex(s)
29
+ s.unpack('H*')[0]
30
+ end
43
31
 
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
32
+ # Hex-decodes a string.
33
+ #
34
+ # @param [String] s
35
+ # String to be decoded.
36
+ #
37
+ # @return [String]
38
+ # Hex-decoded string.
39
+ #
40
+ # @example
41
+ # unhex('353134') #=> '514'
42
+ def unhex(s)
43
+ [s].pack('H*')
44
+ end
59
45
 
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
46
+ # Present number in hex format, same as python hex() do.
47
+ #
48
+ # @param [Integer] n
49
+ # The number.
50
+ #
51
+ # @return [String]
52
+ # The hex format string.
53
+ #
54
+ # @example
55
+ # hex(0) #=> '0x0'
56
+ # hex(-10) #=> '-0xa'
57
+ # hex(0xfaceb00cdeadbeef) #=> '0xfaceb00cdeadbeef'
58
+ def hex(n)
59
+ (n < 0 ? '-' : '') + format('0x%x', n.abs)
60
+ end
72
61
 
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]
62
+ # URL-encodes a string.
63
+ #
64
+ # @param [String] s
65
+ # String to be encoded.
66
+ #
67
+ # @return [String]
68
+ # URL-encoded string.
69
+ #
70
+ # @example
71
+ # urlencode('shikway') #=> '%73%68%69%6b%77%61%79'
72
+ def urlencode(s)
73
+ s.bytes.map { |b| format('%%%02x', b) }.join
74
+ end
75
+
76
+ # URL-decodes a string.
77
+ #
78
+ # @param [String] s
79
+ # String to be decoded.
80
+ # @param [Boolean] ignore_invalid
81
+ # Whether invalid encoding should be ignore.
82
+ # If set to +true+, invalid encoding in input are left intact to output.
83
+ #
84
+ # @return [String]
85
+ # URL-decoded string.
86
+ #
87
+ # @raise [ArgumentError]
88
+ # If +ignore_invalid+ is +false+, and there are invalid encoding in input.
89
+ #
90
+ # @example
91
+ # urldecode('test%20url') #=> 'test url'
92
+ # urldecode('%qw%er%ty') #=> raise ArgumentError
93
+ # urldecode('%qw%er%ty', true) #=> '%qw%er%ty'
94
+ def urldecode(s, ignore_invalid = false)
95
+ res = ''
96
+ n = 0
97
+ while n < s.size
98
+ if s[n] != '%'
99
+ res << s[n]
100
+ n += 1
101
+ else
102
+ cur = s[n + 1, 2]
103
+ if cur =~ /[0-9a-fA-F]{2}/
104
+ res << cur.to_i(16).chr
105
+ n += 3
106
+ elsif ignore_invalid
107
+ res << '%'
97
108
  n += 1
98
109
  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
110
+ raise ArgumentError, 'Invalid input to urldecode'
109
111
  end
110
112
  end
111
- res
112
113
  end
114
+ res
115
+ end
113
116
 
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
117
+ # Converts the argument to an array of bits.
118
+ #
119
+ # @param [String, Integer] s
120
+ # Input to be converted into bits.
121
+ # If input is integer, output would be padded to byte aligned.
122
+ # @param [String] endian
123
+ # Endian for conversion.
124
+ # Can be any value accepted by context (See {Context::ContextType}).
125
+ # @param zero
126
+ # Object representing a 0-bit.
127
+ # @param one
128
+ # Object representing a 1-bit.
129
+ #
130
+ # @return [Array]
131
+ # An array consisting of +zero+ and +one+.
132
+ #
133
+ # @example
134
+ # bits(314) #=> [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0]
135
+ # bits('orz', zero: '-', one: '+').join #=> '-++-++++-+++--+--++++-+-'
136
+ # bits(128, endian: 'little') #=> [0, 0, 0, 0, 0, 0, 0, 1]
137
+ def bits(s, endian: 'big', zero: 0, one: 1)
138
+ context.local(endian: endian) do
139
+ is_little = context.endian == 'little'
140
+ case s
141
+ when String
142
+ v = 'B*'
143
+ v.downcase! if is_little
144
+ s.unpack(v)[0].chars.map { |ch| ch == '1' ? one : zero }
145
+ when Integer
146
+ # TODO(Darkpi): What should we do to negative number?
147
+ raise ArgumentError, 's must be non-negative' unless s >= 0
148
+ r = s.to_s(2).chars.map { |ch| ch == '1' ? one : zero }
149
+ r.unshift(zero) until (r.size % 8).zero?
150
+ is_little ? r.reverse : r
151
+ else
152
+ raise ArgumentError, 's must be either String or Integer'
151
153
  end
152
154
  end
155
+ end
153
156
 
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
157
+ # Simple wrapper around {.bits}, which converts output to string.
158
+ #
159
+ # @param (see .bits)
160
+ #
161
+ # @return [String]
162
+ # The output of {.bits} joined.
163
+ #
164
+ # @example
165
+ # bits_str('GG') #=> '0100011101000111'
166
+ def bits_str(s, endian: 'big', zero: 0, one: 1)
167
+ bits(s, endian: endian, zero: zero, one: one).join
168
+ end
164
169
 
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
170
+ # Reverse of {.bits} and {.bits_str}, convert an array of bits back to string.
171
+ #
172
+ # @param [String, Array<String, Integer, Boolean>] s
173
+ # String or array of bits to be convert back to string.
174
+ # <tt>[0, '0', false]</tt> represents 0-bit, and <tt>[1, '1', true]</tt> represents 1-bit.
175
+ # @param [String] endian
176
+ # Endian for conversion.
177
+ # Can be any value accepted by context (See {Context::ContextType}).
178
+ #
179
+ # @return [String]
180
+ # A string with bits from +s+.
181
+ #
182
+ # @raise [ArgumentError]
183
+ # If input contains value not in <tt>[0, 1, '0', '1', true, false]</tt>.
184
+ #
185
+ # @example
186
+ # unbits('0100011101000111') #=> 'GG'
187
+ # unbits([0, 1, 0, 1, 0, 1, 0, 0]) #=> 'T'
188
+ # unbits('0100011101000111', endian: 'little') #=> "\xE2\xE2"
189
+ def unbits(s, endian: 'big')
190
+ s = s.chars if s.is_a?(String)
191
+ context.local(endian: endian) do
192
+ is_little = context.endian == 'little'
193
+ bytes = s.map do |c|
194
+ case c
195
+ when '1', 1, true then '1'
196
+ when '0', 0, false then '0'
197
+ else raise ArgumentError, "cannot decode value #{c.inspect} into a bit"
191
198
  end
192
- [bytes.join].pack(is_little ? 'b*' : 'B*')
193
199
  end
200
+ [bytes.join].pack(is_little ? 'b*' : 'B*')
194
201
  end
202
+ end
195
203
 
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
204
+ # Reverse the bits of each byte in input string.
205
+ #
206
+ # @param [String] s
207
+ # Input string.
208
+ #
209
+ # @return [String]
210
+ # The string with bits of each byte reversed.
211
+ #
212
+ # @example
213
+ # bitswap('rb') #=> 'NF'
214
+ def bitswap(s)
215
+ unbits(bits(s, endian: 'big'), endian: 'little')
216
+ end
208
217
 
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
218
+ # Reverse the bits of a number, and returns the result as number.
219
+ #
220
+ # @param [Integer] n
221
+ # @param [Integer] bits
222
+ # The bit length of +n+,
223
+ # only the lower +bits+ bits of +n+ would be used.
224
+ # Default to +context.bits+.
225
+ #
226
+ # @return [Integer]
227
+ # The number with bits reversed.
228
+ #
229
+ # @example
230
+ # bitswap_int(217, bits: 8) #=> 155
231
+ def bitswap_int(n, bits: nil)
232
+ context.local(bits: bits) do
233
+ bits = context.bits
234
+ n &= (1 << bits) - 1
235
+ bits_str(n, endian: 'little').ljust(bits, '0').to_i(2)
227
236
  end
237
+ end
228
238
 
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
239
+ # Base64-encodes a string.
240
+ # Do NOT contains those stupid newline (with RFC 4648).
241
+ #
242
+ # @param [String] s
243
+ # String to be encoded.
244
+ #
245
+ # @return [String]
246
+ # Base64-encoded string.
247
+ #
248
+ # @example
249
+ # b64e('desu') #=> 'ZGVzdQ=='
250
+ def b64e(s)
251
+ [s].pack('m0')
252
+ end
242
253
 
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
254
+ # Base64-decodes a string.
255
+ #
256
+ # @param [String] s
257
+ # String to be decoded.
258
+ #
259
+ # @return [String]
260
+ # Base64-decoded string.
261
+ #
262
+ # @example
263
+ # b64d('ZGVzdQ==') #=> 'desu'
264
+ def b64d(s)
265
+ s.unpack('m0')[0]
266
+ end
255
267
 
256
- include ::Pwnlib::Context
268
+ # Find two strings that will xor into a given string, while only using a given alphabet.
269
+ #
270
+ # @param [String, Integer] data
271
+ # The desired string.
272
+ # @param [String] avoid
273
+ # The list of disallowed characters. Defaults to nulls and newlines.
274
+ #
275
+ # @return [(String, String)?]
276
+ # Two strings which will xor to the given string. If no such two strings exist, then nil is returned.
277
+ #
278
+ # @example
279
+ # xor_pair("test") #=> ["\x01\x01\x01\x01", 'udru']
280
+ def xor_pair(data, avoid: "\x00\n")
281
+ data = pack(data) if data.is_a?(Integer)
282
+ alphabet = 256.times.reject { |c| avoid.include?(c.chr) }
283
+ res1 = ''
284
+ res2 = ''
285
+ data.bytes.each do |c1|
286
+ # alphabet.shuffle! if context.randomize
287
+ c2 = alphabet.find { |c| alphabet.include?(c1 ^ c) }
288
+ return nil if c2.nil?
289
+ res1 << c2.chr
290
+ res2 << (c1 ^ c2).chr
291
+ end
292
+ [res1, res2]
257
293
  end
258
294
 
259
- extend ClassMethods
295
+ include ::Pwnlib::Context
260
296
  end
261
297
  end
262
298
  end