Last active
March 23, 2022 21:03
-
-
Save commiebstrd/5da4b4fb61418f924dabe8b0d40494e7 to your computer and use it in GitHub Desktop.
encode shellcode from a sentence or byte array
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!env python3 | |
# -*- coding: utf-8 -*- | |
import sys | |
import string | |
import logging | |
from random import randint | |
from pprint import pprint | |
def get_random(val, max): | |
if val == None: | |
return randint(0, max) | |
tmp = randint(0, max) | |
# shouldnt happen often | |
while tmp == val: # keep looping until we get a different index | |
tmp = randint(0, max) | |
return tmp | |
def encode_byte_additive(sentence, sc_byte, previous=0, random=True): | |
logging.debug(f" finding for byte: {sc_byte}") | |
idx = get_random(0, len(sentence)-1) if random else previous | |
byte_offsets = [] # single list of offsets to sentence | |
offset_total = 0 # allow this to grow as needed | |
# check first byte of offset instead of whole, allow for multiple overflows | |
while (off_byte := offset_total.to_bytes(4, 'little')[0]) != sc_byte: | |
dbg_str = "" | |
if idx >= len(sentence): | |
idx = 0 | |
if isinstance(sentence[idx], str): | |
char_val = ord(sentence[idx]) | |
else: | |
char_val = sentence[idx] # expect bytes/int | |
tmp_val = off_byte + char_val # should NEVER be over 2 bytes | |
if tmp_val == sc_byte: | |
dbg_str += f" found final idx at {idx}" | |
# handle overflows, should be under total desired | |
elif tmp_val > 0xff and tmp_val-0xff < sc_byte: | |
dbg_str += f" adding idx with overflow at {idx}" | |
# now what should be most cases, additions being lower than total | |
elif tmp_val < 0xff and sc_byte <= 0x41 and sc_byte < tmp_val: | |
dbg_str += f" adding larger but not overflowing at {idx}" | |
elif tmp_val < sc_byte: | |
dbg_str += f" adding idx at {idx}" | |
else: | |
# nothing good matched, new index and continue, skip storage | |
#idx = get_random(idx, len(sentence)) | |
idx += 1 | |
continue | |
# store and prep for next loop when found | |
byte_offsets.append(idx) | |
offset_total += char_val | |
idx = get_random(0, len(sentence)-1) if random else idx + 1 | |
logging.debug(f"{dbg_str}, with total={offset_total:X}") | |
return byte_offsets | |
def encode_byte_absolute(sentence, sc_byte, previous=0, random=True): | |
logging.debug(f" finding for byte: {sc_byte}") | |
byte_offsets = [] # single list of offsets to sentence | |
offset_total = 0 # allow this to grow as needed | |
while (off_byte := offset_total.to_bytes(4, 'little')[0]) != sc_byte: | |
idx = get_random(0, len(sentence)-1) if random else previous | |
dbg_str = f" Using idx:{idx} " | |
try: | |
if isinstance(sentence[idx], str): | |
char_val = ord(sentence[idx]) | |
else: | |
char_val = sentence[idx] # expect bytes/int | |
except: | |
print(idx) | |
sys.exit(1) | |
pos = (offset_total + char_val).to_bytes(4, 'little')[0] | |
neg = abs(offset_total - char_val).to_bytes(4, 'little')[0] | |
dbg_str += f"val:{char_val} = {pos}(pos), {neg}(neg)" | |
# find actual match | |
if pos == sc_byte: | |
byte_offsets.append(idx) | |
break | |
elif neg == sc_byte: | |
byte_offsets.append(-idx) # use negative indexes to show sub | |
break | |
# determine best fit | |
if pos < sc_byte: | |
if neg < sc_byte: # both over desired | |
if pos < neg: # closest is smaller value | |
byte_offsets.append(-idx) # negative is closer | |
offset_total = neg | |
dbg_str += " - using neg" | |
else: # pos >= neg | |
byte_offsets.append(idx) | |
offset_total = pos | |
dbg_str += " - using pos" | |
else: # neg > sc_bytes | |
pos_abs = sc_byte - pos | |
neg_abs = neg - sc_byte | |
if pos_abs <= neg_abs: | |
byte_offsets.append(idx) # positive is better fit | |
offset_total = pos | |
dbg_str += " - using pos" | |
else: # pos_abs > neg_abs | |
byte_offsets.append(-idx) | |
offset_total = neg | |
dbg_str += " - using neg" | |
else: # sc_byte < pos | |
if sc_byte < neg: | |
if neg < pos: | |
byte_offsets.append(-idx) # negative is closer | |
offset_total = neg | |
dbg_str += " - using neg" | |
else: | |
byte_offsets.append(idx) | |
offset_total = pos | |
dbg_str += " - using pos" | |
else: # neg < sc_byte | |
pos_abs = pos - sc_byte | |
neg_abs = sc_byte - neg | |
if pos_abs <= neg_abs: | |
byte_offsets.append(idx) # positive is better fit | |
offset_total = pos | |
dbg_str += " - using pos" | |
else: # pos_abs > neg_abs | |
byte_offsets.append(-idx) | |
offset_total = neg | |
dbg_str += " - using neg" | |
logging.debug(dbg_str) | |
previous = idx + 1 if previous < len(sentence)-1 else 0 | |
return byte_offsets | |
def encode(sentence, shellcode, fn, attempts=5, random=True): | |
''' | |
args: | |
sentence - str or byte array of values to look through | |
shellcode - byte array of shellcode to transcribe | |
fn - function doing search method through sentence variable | |
attempts - if random, how many times to try fn and find shortest | |
- if not random, length to iterate through sentence | |
random - if true, uses random locations in sentence each call of fn | |
- if false, starts at 0 | |
returns: | |
list of lists, index of outer list is for each shellcode byte, inner is result of encoder | |
''' | |
logging.info(f"Encoding shellcode[{len(shellcode)}] with sentence[{len(sentence)}] using {fn.__name__}") | |
offsets = [] # really a list of lists | |
for sc_byte in shellcode: | |
shortest = [] | |
for _ in range(0, attempts): | |
byte_offsets = fn(sentence, sc_byte, random=random) | |
# end of while, append found offsets | |
if len(byte_offsets) < len(shortest): | |
shortest = byte_offsets | |
elif not shortest: | |
shortest = byte_offsets | |
logging.info(f" byte({sc_byte}) translated to len({len(shortest)})") | |
offsets.append(shortest) | |
return offsets | |
if __name__ == '__main__': | |
logging.basicConfig(format='%(message)s', encoding='utf-8', level=logging.INFO) | |
sentence = string.printable.encode('utf-8') | |
shellcode = [0xf9, 0x41, 0x1, 0x5, 0x15] | |
encoded = encode(sentence, shellcode, encode_byte_absolute, 1000) | |
print(f"shellcode[{len(shellcode)}][] = {{") | |
for idx,array in enumerate(encoded): | |
s = str(array)[1:-1] | |
print(f" {{ {str(array)[1:-1]} }}, // {shellcode[idx]}") | |
print("}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include<stdio.h> | |
#include<stdlib.h> | |
char sentence[] = "As he crossed toward the pharmacy at the corner he involuntarily turned his head because of a burst of light that had ricocheted from his temple, and saw, with that quick smile with which we greet a rainbow or a rose, a blindingly white parallelogram of sky being unloaded from the van—a dresser with mirrors across which, as across a cinema screen, passed a flawlessly clear reflection of boughs sliding and swaying not arboreally, but with a human vacillation, produced by the nature of those who were carrying this sky, these boughs, this gliding façade."; | |
int offsets[][] = { | |
{1,56,30}, | |
{60,20,100} | |
}; | |
int main(int argc, char** argv) { | |
uint8_t shellcode[] = (uint8_t *) malloc(2*sizeof(uint8_t)); // length offsets * 1 | |
if shellcode <= 0x00 { // zero and neg are errors or undef | |
printf("Failed shellcode malloc!\n"); | |
exit(1); | |
} | |
for (int off_idx=0; off_idx<2; off_idx+=1) { // length offsets | |
int byte_offsets[] = offsets[off_idx]; // temp reference | |
int total = 0; | |
// length of inner offset array for this byte translation | |
for (int off_inner=0, off_inner<sizeof(byte_offsets)/sizeof(int); off_inner+=1) { | |
total += (int) byte_offsets[off_inner]; // offsets[off_idx][off_inner] | |
} | |
// offset index and shellcode index are the same | |
shellcode[off_idx] = (uint8_t) (total & 0xff); | |
} | |
(void *) shellcode (); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
encode() definitely needs some work on choosing best bytes, maybe looping through and storing closest match for a set period of randomly chosen words. still has issues with smaller byte values but a better sentence or multibyte unicode chars that allowed for single digits without non-printable ascii would probably be ideal