rex-encoder 0.1.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 (46) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +1 -0
  3. data.tar.gz.sig +0 -0
  4. data/.gitignore +9 -0
  5. data/.rspec +2 -0
  6. data/.travis.yml +5 -0
  7. data/CODE_OF_CONDUCT.md +49 -0
  8. data/Gemfile +4 -0
  9. data/README.md +32 -0
  10. data/Rakefile +6 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/lib/rex/encoder.rb +15 -0
  14. data/lib/rex/encoder/alpha2.rb +31 -0
  15. data/lib/rex/encoder/alpha2/alpha_mixed.rb +129 -0
  16. data/lib/rex/encoder/alpha2/alpha_upper.rb +138 -0
  17. data/lib/rex/encoder/alpha2/generic.rb +90 -0
  18. data/lib/rex/encoder/alpha2/unicode_mixed.rb +116 -0
  19. data/lib/rex/encoder/alpha2/unicode_upper.rb +123 -0
  20. data/lib/rex/encoder/bloxor/bloxor.rb +327 -0
  21. data/lib/rex/encoder/ndr.rb +90 -0
  22. data/lib/rex/encoder/nonalpha.rb +61 -0
  23. data/lib/rex/encoder/nonupper.rb +64 -0
  24. data/lib/rex/encoder/version.rb +5 -0
  25. data/lib/rex/encoder/xdr.rb +108 -0
  26. data/lib/rex/encoder/xor.rb +69 -0
  27. data/lib/rex/encoder/xor/dword.rb +13 -0
  28. data/lib/rex/encoder/xor/dword_additive.rb +13 -0
  29. data/lib/rex/encoding/xor.rb +20 -0
  30. data/lib/rex/encoding/xor/byte.rb +15 -0
  31. data/lib/rex/encoding/xor/dword.rb +21 -0
  32. data/lib/rex/encoding/xor/dword_additive.rb +92 -0
  33. data/lib/rex/encoding/xor/exceptions.rb +17 -0
  34. data/lib/rex/encoding/xor/generic.rb +146 -0
  35. data/lib/rex/encoding/xor/qword.rb +15 -0
  36. data/lib/rex/encoding/xor/word.rb +21 -0
  37. data/lib/rex/poly.rb +134 -0
  38. data/lib/rex/poly/block.rb +480 -0
  39. data/lib/rex/poly/machine.rb +13 -0
  40. data/lib/rex/poly/machine/machine.rb +830 -0
  41. data/lib/rex/poly/machine/x86.rb +509 -0
  42. data/lib/rex/poly/register.rb +101 -0
  43. data/lib/rex/poly/register/x86.rb +41 -0
  44. data/rex-encoder.gemspec +31 -0
  45. metadata +248 -0
  46. metadata.gz.sig +0 -0
@@ -0,0 +1,15 @@
1
+ # -*- coding: binary -*-
2
+
3
+ require 'rex/encoding/xor/generic'
4
+
5
+ module Rex
6
+ module Encoding
7
+ module Xor
8
+
9
+ class Byte < Generic
10
+
11
+ def Byte.keysize
12
+ 1
13
+ end
14
+
15
+ end end end end # Byte/Xor/Encoding/Rex
@@ -0,0 +1,21 @@
1
+ # -*- coding: binary -*-
2
+
3
+ require 'rex/encoding/xor/generic'
4
+
5
+ #
6
+ # Routine for xor encoding a buffer by a 2-byte (intel word) key. The perl
7
+ # version used to pad this buffer out to a 2-byte boundary, but I can't think
8
+ # of a good reason to do that anymore, so this doesn't.
9
+ #
10
+
11
+ module Rex
12
+ module Encoding
13
+ module Xor
14
+
15
+ class Dword < Generic
16
+
17
+ def Dword.keysize
18
+ 4
19
+ end
20
+
21
+ end end end end # Dword/Xor/Encoding/Rex
@@ -0,0 +1,92 @@
1
+ # -*- coding: binary -*-
2
+
3
+ require 'rex/encoding/xor/exceptions'
4
+ require 'rex/encoding/xor/generic'
5
+
6
+ #
7
+ # Routine for xor encoding a buffer by a 2-byte (intel word) key. The perl
8
+ # version used to pad this buffer out to a 2-byte boundary, but I can't think
9
+ # of a good reason to do that anymore, so this doesn't.
10
+ #
11
+
12
+ module Rex
13
+ module Encoding
14
+ module Xor
15
+
16
+ class DwordAdditive < Generic
17
+
18
+ def DwordAdditive.keysize
19
+ 4
20
+ end
21
+
22
+ def DwordAdditive._packspec
23
+ 'V'
24
+ end
25
+
26
+ def DwordAdditive.pack_key(key)
27
+ return [ key ].pack(_packspec)
28
+ end
29
+ def DwordAdditive.unpack_key(key)
30
+ return key.unpack(_packspec)[0]
31
+ end
32
+
33
+ # hook in the key mutation routine of encode for the additive feedback
34
+ def DwordAdditive._encode_mutate_key(buf, key, pos, len)
35
+ if (pos + 1) % len == 0
36
+ # add the last len bytes (in this case 4) with the key,
37
+ # dropping off any overflow
38
+ key = pack_key(
39
+ unpack_key(key) + unpack_key(buf[pos - (len - 1), len]) &
40
+ (1 << (len << 3)) - 1
41
+ )
42
+ end
43
+
44
+ return key
45
+ end
46
+
47
+ #
48
+ # I realize this algorithm is broken. We invalidate some keys
49
+ # in _find_bad_keys that could actually be perfectly fine. However,
50
+ # it seems to work ok for now, and this is all just a lame adhoc method.
51
+ # Maybe someday we can revisit this and make it a bit less ghetto...
52
+ #
53
+
54
+ def DwordAdditive._find_good_key(data, badkeys, badchars)
55
+
56
+ ksize = keysize
57
+ kstart = ""
58
+ ksize.times { kstart << rand(256) } # random key starting place
59
+
60
+ key = kstart.dup
61
+
62
+ #
63
+ # now for the ghettoness of an algorithm:
64
+ # try the random key we picked
65
+ # if the key failed, figure out which key byte corresponds
66
+ # increment that key byte
67
+ # if we wrapped a byte all the way around, fail :(
68
+ #
69
+
70
+ loop do
71
+ # ok, try to encode it, any bad chars present?
72
+ pos = _check(data, key, badchars)
73
+
74
+ # yay, no problems, we found a key!
75
+ break if !pos
76
+
77
+ strip = pos % ksize
78
+
79
+ # increment the offending key byte
80
+ key[strip] = key[strip] + 1 & 0xff
81
+
82
+ # We wrapped around!
83
+ if key[strip] == kstart[strip]
84
+ raise KeySearchError, "Key space exhausted on strip #{strip}!", caller
85
+ end
86
+ end
87
+
88
+ return key
89
+ end
90
+
91
+ end end end end # DwordAdditive/Xor/Encoding/Rex
92
+
@@ -0,0 +1,17 @@
1
+ # -*- coding: binary -*-
2
+
3
+ module Rex
4
+ module Encoding
5
+ module Xor
6
+
7
+ module Exception
8
+
9
+ end
10
+
11
+ class KeySearchError < ::Exception
12
+ include Exception
13
+ MSG = "Error finding a key."
14
+ end
15
+
16
+ end end end
17
+
@@ -0,0 +1,146 @@
1
+ # -*- coding: binary -*-
2
+
3
+ require 'rex/encoding/xor/exceptions'
4
+ require 'rex/text'
5
+
6
+ module Rex
7
+ module Encoding
8
+ module Xor
9
+
10
+ class Generic
11
+
12
+ def Generic.keysize
13
+ # special case:
14
+ # 0 means we encode based on the length of the key
15
+ # we don't enforce any perticular key length
16
+ return 0
17
+ end
18
+
19
+ #
20
+ # Now for some internal check methods
21
+ #
22
+
23
+ # hook stylies!
24
+ # return index of offending byte or nil
25
+ def Generic._check(data, key, badchars)
26
+ return _check_key(key, badchars) || _check_encode(data, key, badchars)
27
+ end
28
+ def Generic._check_key(key, badchars)
29
+ return Rex::Text.badchar_index(key, badchars)
30
+ end
31
+ def Generic._check_encode(data, key, badchars)
32
+ return Rex::Text.badchar_index(encode(data, key), badchars)
33
+ end
34
+
35
+ def Generic.find_key(data, badchars)
36
+ return _find_good_key(data, _find_bad_keys(data, badchars), badchars)
37
+ end
38
+
39
+ # !!! xxx MAKE THESE PRIVATE
40
+
41
+ #
42
+ # Find a list of bytes that can't be valid xor keys, from the data and badchars.
43
+ # This returns a Array of hashes, length keysize
44
+ #
45
+ def Generic._find_bad_keys(data, badchars)
46
+
47
+ ksize = keysize
48
+
49
+ # array of hashes for the bad characters based
50
+ # on their position in the data
51
+ badkeys = [ ]
52
+ ksize.times { badkeys << { } }
53
+
54
+ badchars.each_byte { |badchar|
55
+ pos = 0
56
+ data.each_byte { |char|
57
+ badkeys[pos % ksize][char ^ badchar] = true
58
+ pos += 1
59
+ }
60
+ }
61
+
62
+ return badkeys
63
+ end
64
+
65
+ #
66
+ # (Hopefully) find a good key, from badkeys and badchars
67
+ #
68
+ def Generic._find_good_key(data, badkeys, badchars)
69
+
70
+ ksize = keysize
71
+ strip = 0
72
+ key = ""
73
+
74
+ while strip < keysize
75
+
76
+ kbyte = rand(256)
77
+
78
+ catch(:found_kbyte) do
79
+ 256.times {
80
+
81
+ if !badkeys[strip][kbyte] && !badchars[kbyte.chr]
82
+ throw :found_kbyte
83
+ end
84
+
85
+ kbyte = (kbyte + 1) & 0xff
86
+ }
87
+
88
+ raise KeySearchError, "Exhausted byte space for strip #{strip}!", caller
89
+ end
90
+
91
+ key << kbyte
92
+ strip += 1
93
+ end
94
+
95
+ # ok, we should have a good key now, lets double check...
96
+ if _check(data, key, badchars)
97
+ raise KeySearchError, "Key found, but bad character check failed!", caller
98
+ end
99
+
100
+ return key
101
+ end
102
+
103
+ def Generic.encode(buf, key)
104
+
105
+ if !key.kind_of?(String)
106
+ raise ::ArgumentError, "Key must be a string!", caller
107
+ end
108
+
109
+ len = key.length
110
+
111
+ if len == 0
112
+ raise ::ArgumentError, "Zero key length!", caller
113
+ end
114
+
115
+ if keysize != 0 && keysize != len
116
+ raise ::ArgumentError, "Key length #{len}, expected #{keysize}", caller
117
+ end
118
+
119
+ encoded = ""
120
+ pos = 0
121
+
122
+ while pos < buf.length
123
+ encoded += (buf[pos,1].unpack("C*")[0] ^ key[pos % len, 1].unpack("C*")[0]).chr
124
+ key = _encode_mutate_key(buf, key, pos, len)
125
+ pos += 1
126
+ end
127
+
128
+ return [ encoded, key ]
129
+
130
+ end
131
+
132
+ # kind of ghetto, but very convenient for mutating keys
133
+ # by default, do no key mutations
134
+ def Generic._encode_mutate_key(buf, key, pos, len)
135
+ return key
136
+ end
137
+
138
+ # maybe a bit a smaller of method name?
139
+ def Generic.find_key_and_encode(data, badchars)
140
+ key = find_key(data, badchars)
141
+ enc, fkey = encode(data, key)
142
+ return [ enc, key, fkey ]
143
+ end
144
+
145
+
146
+ end end end end # Generic/Xor/Encoding/Rex
@@ -0,0 +1,15 @@
1
+ # -*- coding: binary -*-
2
+
3
+ require 'rex/encoding/xor/generic'
4
+
5
+ module Rex
6
+ module Encoding
7
+ module Xor
8
+
9
+ class Qword < Generic
10
+
11
+ def Qword.keysize
12
+ 8
13
+ end
14
+
15
+ end end end end
@@ -0,0 +1,21 @@
1
+ # -*- coding: binary -*-
2
+
3
+ require 'rex/encoding/xor/generic'
4
+
5
+ #
6
+ # Routine for xor encoding a buffer by a 2-byte (intel word) key. The perl
7
+ # version used to pad this buffer out to a 2-byte boundary, but I can't think
8
+ # of a good reason to do that anymore, so this doesn't.
9
+ #
10
+
11
+ module Rex
12
+ module Encoding
13
+ module Xor
14
+
15
+ class Word < Generic
16
+
17
+ def Word.keysize
18
+ 2
19
+ end
20
+
21
+ end end end end # Word/Xor/Encoding/Rex
@@ -0,0 +1,134 @@
1
+ # -*- coding: binary -*-
2
+ module Rex
3
+ module Poly
4
+
5
+ require 'rex/poly/register'
6
+ require 'rex/poly/block'
7
+ require 'rex/poly/machine'
8
+
9
+ ###
10
+ #
11
+ # This class encapsulates the state of a single polymorphic block set
12
+ # generation. It tracks the current set of consumed registers, the linear
13
+ # list of blocks generated, the end-result buffer, and the phase of
14
+ # generation. The fields exposed by the State class are intended for use only
15
+ # by the polymorphic generation subsystem and should not be modified directly.
16
+ #
17
+ ###
18
+ class State
19
+
20
+ #
21
+ # Initializes the polymorphic generation state.
22
+ #
23
+ def initialize
24
+ @block_list = nil
25
+ reset
26
+ end
27
+
28
+ #
29
+ # Resets the generation state to have a plain start by clearing all
30
+ # consumed registers, resetting the polymorphic buffer back to its
31
+ # beginning and destroying any block generation state.
32
+ #
33
+ def reset
34
+ # Reset the generation flag on any blocks in the block list
35
+ @block_list.each { |block|
36
+ block[0].generated = false
37
+ } if (@block_list)
38
+
39
+ @regnums = Hash.new
40
+ @buffer = ''
41
+ @block_list = []
42
+ @curr_offset = 0
43
+ @first_phase = true
44
+ @badchars = nil
45
+ end
46
+
47
+ #
48
+ # Returns true if the supplied register number is already consumed.
49
+ #
50
+ def consumed_regnum?(regnum)
51
+ @regnums[regnum]
52
+ end
53
+
54
+ #
55
+ # Consumes a register number, thus removing it from the pool that can be
56
+ # assigned. The consumed register number is returned to the caller.
57
+ #
58
+ def consume_regnum(regnum)
59
+ raise RuntimeError, "Register #{regnum} is already consumed." if (consumed_regnum?(regnum))
60
+
61
+ @regnums[regnum] = true
62
+
63
+ regnum
64
+ end
65
+
66
+ #
67
+ # Acquires a register number that has not already been consumed from the
68
+ # supplied register number set and consumes it, returning the selected
69
+ # register number to the caller. The register number is selected from the
70
+ # set at random.
71
+ #
72
+ def consume_regnum_from_set(regnum_set)
73
+ # Pick a random starting point within the supplied set.
74
+ idx = rand(regnum_set.length)
75
+
76
+ # Try each index in the set.
77
+ regnum_set.length.times { |x|
78
+ regnum = regnum_set[(idx + x) % regnum_set.length]
79
+
80
+ next if (consumed_regnum?(regnum))
81
+
82
+ return consume_regnum(regnum)
83
+ }
84
+
85
+ # If we get through the entire iteration without finding a register,
86
+ # then we are out of registers to assign.
87
+ raise RuntimeError, "No registers are available to consume from the set"
88
+ end
89
+
90
+ #
91
+ # Eliminates a register number from the consumed pool so that it can be
92
+ # used in the future. This happens after a block indicates that a register
93
+ # has been clobbered.
94
+ #
95
+ def defecate_regnum(regnum)
96
+ @regnums.delete(regnum)
97
+ end
98
+
99
+ #
100
+ # The buffer state for the current polymorphic generation. This stores the
101
+ # end-result of a call to generate on a LogicalBlock.
102
+ #
103
+ attr_accessor :buffer
104
+
105
+ #
106
+ # The linear list of blocks that is generated by calling the generate
107
+ # method on a LogicalBlock.
108
+ #
109
+ attr_accessor :block_list
110
+
111
+ #
112
+ # The current offset into the polymorphic buffer that is being generated.
113
+ # This is updated as blocks are appended to the block_list.
114
+ #
115
+ attr_accessor :curr_offset
116
+
117
+ #
118
+ # A boolean field that is used by the LogicalBlock class to track whether
119
+ # or not it is in the first phase (generating the block list), or in the
120
+ # second phase (generating the polymorphic buffer). This phases are used
121
+ # to indicate whether or not the offset_of and regnum_of methods will
122
+ # return actual results.
123
+ #
124
+ attr_accessor :first_phase
125
+
126
+ #
127
+ # Characters to avoid when selecting permutations, if any.
128
+ #
129
+ attr_accessor :badchars
130
+
131
+ end
132
+
133
+ end
134
+ end