#!/usr/bin/env python
#
# Script name     : objdump2shellcode.py
# Version         : 3.0
# Created date    : 5/27/2017
# Last update     : 7/27/2017
# Author          : wetw0rk
# Python version  : 2.7
# Description     : This is 1000000000 times easier than manually
#                   copying/pasting custom shellcode generated by
#                   objdump. python, perl, c, and nasm types of
#		    output supported
#

import os, sys, subprocess, argparse

snipit          = []
instructions    = []
opcodes         = []
no_junk         = []
bad_char_line   = ""
ops		= ""
bad		= ""

def clean_output(split, badchars):

	global bad_char_line, instructions
	global snipit, opcodes, no_junk, ops
	global bad

	for i in range(len(split)):
		# split up every line by a [tab] and remove address
		line = split[i].split('\t')[1:3]
		# get rid of blank lines
		if len(line) > 0:
			snipit += line
		else:
			pass
	# separate opcodes and instructions
	list_len = len(snipit)
	for i in range(list_len):
		if (i & 1) == 1:
			instructions += snipit[i],
		else:
			opcodes += snipit[i],

	# cut off the junk!!!
	for i in range(len(opcodes)):
		no_junk  += opcodes[i].rstrip(" "),


	# this will aid in bad character verification
	for i in range(len(opcodes)):
		opcodes[i] = opcodes[i].rstrip(" ")

	for i in range(len(opcodes)):
		ops += "\\x%s" % opcodes[i].replace(" ", "\\x")

	try:
		stop_now = badchars.split(",")
		for i in range(len(stop_now)):
			if stop_now[i] in ops:
				bad += "%s" % stop_now[i]
		if len(bad) > 1:
			print "Bad characters spotted: %s" % (bad)
		else:
			pass
	except:
		pass

	print "Payload size: %d bytes" % (len(ops)/4)

	return

class format_dump():

	def __init__(self, intel_dump, mode, badchars):
		self.intel_dump		= intel_dump
		self.mode		= mode
		self.badchars		= badchars

	def tact_dump(self):

		split = self.intel_dump.split("\n")

		# apart stuff
		clean_output(split, self.badchars)

		if self.mode == "python":
			print 'buf = ""'
			splits = [ops[x:x+60] for x in range(0,len(ops),60)]
			for i in range(len(splits)):
				print "buf += \"" + splits[i] + "\""

		if self.mode == "c":
			print "unsigned char buf[] = "
			splits = [ops[x:x+60] for x in range(0,len(ops),60)]
			for i in range(len(splits)):
				if i == (len(splits) -1):
					print "\"" + splits[i] + "\"" + ";"
				else:
					print "\"" + splits[i] + "\""

		if self.mode == "perl":
			print "my $buf ="
			splits = [ops[x:x+60] for x in range(0,len(ops),60)]
			for i in range(len(splits)):
				if i == (len(splits) -1):
					print "\"" + splits[i] + "\"" + ";"
				else:
					print "\"" + splits[i] + "\"" + " ."

		if self.mode == "nasm":
			nasm = ""
			asm_ops = ops.split("\\x")
			for i in range(len(asm_ops)):
				if asm_ops[i] == '':
					pass
				else:
					nasm += "0x%s," % (asm_ops[i])
			split = [nasm[x:x+40] for x in range(0,len(nasm),40)]
			for i in range(len(split)):
				snip = len(split[i]) - 1
				print "db " + split[i][:snip]

	def info_dump(self):

		split = self.intel_dump.split("\n")

		# apart stuff
		clean_output(split, self.badchars)

		if self.mode == "python":
			print 'buf = ""'
			for i in range(len(no_junk)):
				print("buf += \"\\x%s\"\t# %s" % (
					no_junk[i].replace(" ", "\\x"),
					instructions[i])
				).expandtabs(30)

		if self.mode == "c":
			print "unsigned char buf[] = "
			for i in range(len(no_junk)):
				if i != (len(no_junk)-1):
					print("\"\\x%s\"\t// %s" % (
						no_junk[i].replace(" ", "\\x"),
						instructions[i])
					).expandtabs(30)
				else:
					print("\"\\x%s\";\t// %s" % (
						no_junk[i].replace(" ", "\\x"),
						instructions[i])
					).expandtabs(30)

		if self.mode == "perl":
			print "my $buf ="
			for i in range(len(no_junk)):
				if i != (len(no_junk)-1):
					print("\"\\x%s\".\t# %s" % (
						no_junk[i].replace(" ", "\\x"),
						instructions[i])
					).expandtabs(30)
				else:
					print("\"\\x%s\";\t# %s" % (
						no_junk[i].replace(" ", "\\x"),
						instructions[i])
					).expandtabs(30)

def begin_dump(dumpfile, mode, badchars, comment_code):

	# detect if the file exists
	if os.path.isfile(dumpfile) is False:
		print "File non-existent!"
		sys.exit()

	# run objdump to disassemble the binary
	try:
		intel_dump = subprocess.Popen(['objdump', '-D', dumpfile, '-M', 'intel'],
			stdout=subprocess.PIPE).communicate()[0]
	except:
		print "[-] error running command"
		sys.exit()

	# format the output dump accordingly
	if comment_code:
		format = format_dump(intel_dump, mode, badchars)
		format.info_dump()
	else:
		format = format_dump(intel_dump, mode, badchars)
		format.tact_dump()

def main():
	# handle command line arguments
	parser = argparse.ArgumentParser()
	parser.add_argument("-d", "--dump", help="binary to dump shellcode from")
	parser.add_argument("-f", "--format", help="format of dump c, python, perl, nasm")
	parser.add_argument("-b", "--badchar", help="seperate bad chars like so \\x00,\\x0a")
	parser.add_argument("-c", "--comment", help="comments the shellcode output", action="store_true")
	args = parser.parse_args()
	# assign arguments
	dumpfile	= args.dump
	mode		= args.format
	badchars	= args.badchar
	comment_code	= args.comment
	# pass to function (thank you @justsam)
	if dumpfile > 1:
		begin_dump(dumpfile, mode, badchars, comment_code)
	else:
		print(parser.print_help())

if __name__ == '__main__':
	main()
