- Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathshellcode_encoder.py
More file actions
Latest commit
269 lines (218 loc) · 10.2 KB
/
Copy pathshellcode_encoder.py
File metadata and controls
269 lines (218 loc) · 10.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
#!/usr/bin/env python3
"""
Exploit-Dev-Toolkit — Shellcode Encoder
A reference tool for shellcode encoding and manipulation techniques.
This module is documentation-first. Run it to see conceptual guides and
usage for XOR encoding, alphanumeric shellcode, and NOP sled techniques.
Usage:
python shellcode_encoder.py # Print full reference
python shellcode_encoder.py --xor # XOR encoding reference
python shellcode_encoder.py --alphanumeric # Alphanumeric reference
python shellcode_encoder.py --nop # NOP sled reference
python shellcode_encoder.py --xor-encode <hex> # XOR-encode raw shellcode
python shellcode_encoder.py --nop-gen <count> # Generate a NOP sled
"""
importargparse
importrandom
importstruct
importsys
HELP_XOR="""
╔══════════════════════════════════════════════════════════════════════╗
║ XOR Encoding ║
╚══════════════════════════════════════════════════════════════════════╝
Concept:
XOR encoding transforms shellcode by XOR-ing each byte with a single
key byte (or multi-byte key). A small decoder stub placed before the
encoded payload re-XORs every byte at runtime to restore the original.
This evades signature-based detection because the encoded payload
looks like random bytes rather than known shellcode patterns.
Single-byte XOR:
Encoded[i] = Shellcode[i] XOR Key
Shellcode[i] = Encoded[i] XOR Key (XOR is its own inverse)
Decoder stub (NASM x86):
xor_decode:
pop esi ; address of encoded shellcode
xor ecx, ecx
mov cl, LENGTH ; shellcode length
loop:
xor byte [esi+ecx-1], KEY
loop loop
jmp esi
Multi-byte XOR (rolling key):
Encoded[i] = Shellcode[i] XOR Key[i % key_len]
Harder to detect; decoder stub loops through key bytes.
Pitfalls:
- Null bytes (0x00) in the decoder stub may break string-based exploits.
- Key 0x00 = no encoding (avoid).
- Key choice matters: a key equal to common bytes (e.g., 0x90 for NOP)
may produce many nulls in output.
Tools / Libraries:
- msfvenom: msfvenom -p linux/x64/exec ... -e x86/xor_mixed
- pwntools: from pwn import *; encoded = xor(shellcode, key)
- Custom: provided by --xor-encode below
"""
HELP_ALPHANUMERIC="""
╔══════════════════════════════════════════════════════════════════════╗
║ Alphanumeric Shellcode ║
╚══════════════════════════════════════════════════════════════════════╝
Concept:
Alphanumeric (aka printable) shellcode consists only of bytes in the
ASCII printable range (0x20–0x7E). This allows the payload to pass
through filters that reject non-printable characters — common in
input validation, WAFs, and some protocol parsers.
The constraint is severe: only ~95 byte values out of 256 are allowed.
This means the decoder stub itself must be alphanumeric.
Techniques:
1. SUB / ADD / XOR with alphanumeric immediates
- On x86, SUB EAX, 0x3D3D3D3D is printable.
- Chain enough arithmetic to build arbitrary values.
2. Self-modifying alphanumeric code
- Write non-printable bytes into memory using only printable
instructions, then jump to the reconstructed shellcode.
3. Use of INC/ DEC / PUSH / POP variants that fall in the printable range:
- 0x50–0x57: PUSH register (printable: PUSH EAX = 0x50 ... PUSH EDI)
- 0x58–0x5F: POP register (POP EAX = 0x58 ... POP EDI)
- 0x6A: PUSH imm8
- 0x68: PUSH imm32
Generating alphanumeric shellcode:
- msfvenom: msfvenom -p windows/exec CMD=calc -e x86/alpha_mixed
- ADMmutate (older)
- Custom encoder walking the XOR/ADD/SUB spiral
Pitfalls:
- Alphanumeric shellcode is typically 3–10x larger than the original.
- Only well-studied for x86 32-bit; x64 support is more limited.
- Some filters reject uppercase vs lowercase, shrinking the pool further.
"""
HELP_NOP="""
╔══════════════════════════════════════════════════════════════════════╗
║ NOP Sleds ║
╚══════════════════════════════════════════════════════════════════════╝
Concept:
A NOP sled (NOP slide) is a sequence of no-operation instructions
placed before the shellcode in a buffer overflow exploit. When the
instruction pointer lands anywhere in the sled, it slides forward
until it hits the actual shellcode.
This dramatically increases reliability: instead of needing an exact
return-address hit, *any* address within the sled will work.
Common NOP instructions:
x86 / x64:
Byte | Mnemonic
──────┼────────────────────
0x90 | NOP (1 byte)
0x66 | operand-size override prefix (harmless in sled)
0x90 | repeated = long NOP sled
Multi-byte NOP-like instructions (undirected, no side effects):
0x87 0xDB | XCHG EAX, EBX
0x96 | XCHG EAX, ESI
0x97 | XCHG EAX, EDI
0x42 | INC EDX (safe if EDX value doesn't matter)
0x40 | INC EAX (safe if EAX value doesn't matter)
Modern compilers emit multi-byte NOPs for alignment:
0x66 0x90 | 2-byte NOP
0x0F 0x1F 0x00 | 3-byte NOP
0x0F 0x1F 0x40 0x00 | 4-byte NOP
0x0F 0x1F 0x44 0x00 0x00 | 5-byte NOP
Evasion with NOP sleds:
- Use different NOP-like instructions to evade NOP-sled signatures.
- Interleave decoded multi-byte NOPs to avoid byte-level pattern match.
- Randomize sled instructions per-run to break static signatures.
Example payload layout:
[AAAA...padding...] [return_addr] [NOP SLED] [SHELLCODE]
"""
defxor_encode(shellcode_hex: str, key: int=0x55) ->str:
"""
XOR-encode shellcode given as hex string.
Returns hex string of encoded shellcode.
"""
try:
raw=bytes.fromhex(shellcode_hex)
exceptValueError:
print(f"[!] Invalid hex string: {shellcode_hex}")
sys.exit(1)
ifkey<0orkey>255:
print(f"[!] Key must be 0-255, got {key}")
sys.exit(1)
encoded=bytes([b^keyforbinraw])
returnencoded.hex()
defgenerate_nop_sled(count: int, variant: str="basic") ->str:
"""
Generate a NOP sled of `count` bytes, as a hex string.
Variants: basic (0x90), mixed (random NOP-like), multi-byte
"""
ifcount<1:
return""
# Single-byte NOP-like instructions
NOP_BYTES= [0x90, 0x42, 0x40, 0x96, 0x97, 0x50, 0x58]
# Multi-byte NOP sequences
MULTI_NOPS= [
bytes([0x66, 0x90]),
bytes([0x0F, 0x1F, 0x00]),
bytes([0x0F, 0x1F, 0x40, 0x00]),
bytes([0x0F, 0x1F, 0x44, 0x00, 0x00]),
]
ifvariant=="basic":
sled=bytes([0x90] *count)
elifvariant=="mixed":
parts= []
remaining=count
whileremaining>0:
b=random.choice(NOP_BYTES)
parts.append(bytes([b]))
remaining-=1
sled=b"".join(parts)
elifvariant=="multi":
parts= []
remaining=count
whileremaining>0:
chosen=random.choice(MULTI_NOPS)
iflen(chosen) <=remaining:
parts.append(chosen)
remaining-=len(chosen)
else:
parts.append(bytes([0x90]))
remaining-=1
sled=b"".join(parts)
else:
print(f"[!] Unknown variant: {variant} (use: basic, mixed, multi)")
sys.exit(1)
returnsled.hex()
defmain():
parser=argparse.ArgumentParser(
description="Exploit-Dev-Toolkit — Shellcode Encoder Reference"
)
group=parser.add_mutually_exclusive_group()
group.add_argument("--xor", action="store_true", help="Show XOR encoding reference")
group.add_argument("--alphanumeric", action="store_true", help="Show alphanumeric shellcode reference")
group.add_argument("--nop", action="store_true", help="Show NOP sled reference")
group.add_argument("--xor-encode", metavar="HEX_SHELLCODE", help="XOR-encode shellcode (hex input)")
group.add_argument("--nop-gen", metavar="COUNT", type=int, help="Generate a NOP sled of COUNT bytes")
parser.add_argument("--key", type=int, default=0x55, help="XOR key (0-255, default: 0x55)")
parser.add_argument("--variant", default="basic", choices=["basic", "mixed", "multi"],
help="NOP sled variant (default: basic)")
args=parser.parse_args()
ifargs.xor:
print(HELP_XOR)
elifargs.alphanumeric:
print(HELP_ALPHANUMERIC)
elifargs.nop:
print(HELP_NOP)
elifargs.xor_encode:
result=xor_encode(args.xor_encode, args.key)
print(f"Original: {args.xor_encode}")
print(f"Key: 0x{args.key:02x} ({args.key})")
print(f"Encoded: {result}")
print(f"Length: {len(bytes.fromhex(result))} bytes")
elifargs.nop_gen:
result=generate_nop_sled(args.nop_gen, args.variant)
print(f"NOP sled ({args.nop_gen} bytes, variant: {args.variant}):")
print(result)
else:
print(HELP_XOR)
print(HELP_ALPHANUMERIC)
print(HELP_NOP)
print()
print("─"*66)
print("Quick reference: run with --xor, --alphanumeric, --nop,")
print("--xor-encode <hex>, or --nop-gen <count>")
if__name__=="__main__":
main()