rex-encoder 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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