scrap growth one nop at a time, store disassembly

master
JoYo 2019-02-20 23:32:34 +00:00
parent 42c7364589
commit d6f1f12669
6 changed files with 80 additions and 40 deletions

View File

@ -2,5 +2,5 @@ FROM ubuntu:bionic
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y \ RUN apt-get update && apt-get install -y \
python3-sqlalchemy \ python3-capstone \
yasm python3-sqlalchemy

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from .run import sins from .run import sins
from .mutation import generation, flip, seed_shell from .mutation import generation, mutate
from .orm import db_config, ScrapNode from .orm import db_config, ScrapNode

13
sins/disassemble.py Normal file
View File

@ -0,0 +1,13 @@
#! /usr/bin/env python3
from capstone import Cs, CS_ARCH_X86, CS_MODE_64
import json
capstone = Cs(CS_ARCH_X86, CS_MODE_64)
def disasm(shellcode: bytes)->str:
opcodes = list()
for opcode in capstone.disasm(shellcode, 0):
opcodes.append([opcode.mnemonic, opcode.op_str])
return opcodes

View File

@ -4,14 +4,6 @@ from random import randint
import ctypes import ctypes
import mmap import mmap
template_shell = b''.join([
b'\x55', # push rbp
b'\x48\x89\xe5', # mov rbp,rsp
b'\x48\x89\x7d\xf8', # mov QWORD [rbp-0x8],rdi
b'\x48\x8b\x45\xf8', # mov rax,QWORD [rbp-0x8]
b'\x5d', # pop rbp
b'\xc3']) # ret
seed_shell = b''.join([ seed_shell = b''.join([
b'\x55', b'\x55',
b'\x48\x89\xe5', b'\x48\x89\xe5',
@ -22,8 +14,19 @@ seed_shell = b''.join([
b'\x5d', b'\x5d',
b'\xc3']) b'\xc3'])
# use template to declutter output, todo
seed_shell = b''.join([
b'\x55', # push rbp
b'\x48\x89\xe5', # mov rbp,rsp
b'\x90' * 8, # nop
b'\x48\x89\x7d\xf8', # mov QWORD [rbp-0x8],rdi
b'\x90' * 8, # nop
b'\x48\x8b\x45\xf8', # mov rax,QWORD [rbp-0x8]
b'\x5d', # pop rbp
b'\xc3']) # ret
def flip(shellcode: bytes):
def mutate(shellcode: bytes):
shellcode = bytearray(shellcode) shellcode = bytearray(shellcode)
offset = randint(0, len(shellcode) - 1) offset = randint(0, len(shellcode) - 1)
flip = randint(0, 255) flip = randint(0, 255)
@ -47,3 +50,17 @@ def generation(queue: Queue, shellcode: bytes):
result = function(shellcode_len) result = function(shellcode_len)
queue.put(result) queue.put(result)
def growth(*, shellcode: bytes, length: int) -> bytes:
shellcode = bytearray(shellcode)
# slow growth and stop shrinking
if length > len(shellcode):
growth = 1
else:
growth = 0
shellcode = shellcode + (b'\x90' * growth)
return bytes(shellcode)

View File

@ -6,11 +6,11 @@ from sqlalchemy import LargeBinary, Column, ForeignKey, Integer, String, DateTim
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session, relationship, backref from sqlalchemy.orm import Session, relationship, backref
from sqlalchemy.orm.collections import attribute_mapped_collection from sqlalchemy.orm.collections import attribute_mapped_collection
import logging import json
from .disassemble import disasm
logger = logging.getLogger('sins')
now = '{0:%Y%m%dT%H%M%S}'.format(datetime.utcnow()) now = '{0:%Y%m%dT%H%M%S}'.format(datetime.utcnow())
Base = declarative_base() Base = declarative_base()
@ -29,7 +29,7 @@ class ScrapNode(Base):
mtime = Column(DateTime, onupdate=datetime.utcnow) mtime = Column(DateTime, onupdate=datetime.utcnow)
parent_id = Column(Integer, ForeignKey(id)) parent_id = Column(Integer, ForeignKey(id))
checksum = Column(String) checksum = Column(String)
stdout = Column(String) disasm = Column(String)
image = Column(LargeBinary) image = Column(LargeBinary)
children = relationship( children = relationship(
@ -43,16 +43,18 @@ class ScrapNode(Base):
self.image = child self.image = child
self.length = len(child) self.length = len(child)
self.sha1sum self.sha1sum
self.disasm = disasm(child)
def __repr__(self): def __repr__(self):
values = { values = {
'checksum': self.checksum, 'checksum': self.checksum,
'length': self.length, 'length': self.length,
'disasm': self.disasm,
'parent_id': self.parent_id, 'parent_id': self.parent_id,
'id': self.id, 'id': self.id,
} }
return str(values) return json.dumps(values, indent=1)
@property @property
def sha1sum(self): def sha1sum(self):
@ -64,3 +66,12 @@ class ScrapNode(Base):
self.checksum = checksum.hexdigest() self.checksum = checksum.hexdigest()
return self.checksum return self.checksum
def disasm(shellcode: bytes) -> str:
opcodes = list()
for opcode in capstone.disasm(shellcode, 0):
opcodes += f'{opcode.mnemonic} {opcode.op_str}\n'
return opcodes

View File

@ -8,7 +8,7 @@ from sqlalchemy import exists, desc
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
import logging import logging
from .mutation import generation, flip, seed_shell from .mutation import generation, mutate, seed_shell, growth
from .orm import db_config, ScrapNode from .orm import db_config, ScrapNode
@ -48,18 +48,6 @@ def sins():
logger.info(now) logger.info(now)
seed_data = seed_shell
if args.seed:
seed = Path(args.seed)
with seed.open('rb') as seed_file:
seed_data = seed_file.read()
seed = ScrapNode(child=seed_data)
logger.debug(f'seed:\n{seed}')
if args.output: if args.output:
db_path = Path(f'{args.output}/sins.sqlite') db_path = Path(f'{args.output}/sins.sqlite')
else: else:
@ -68,38 +56,48 @@ def sins():
session = db_config(db_path) session = db_config(db_path)
logger.info(f'db_path: {db_path}') logger.info(f'db_path: {db_path}')
recent = session.query(ScrapNode).order_by(desc('ctime')).first()
if args.seed: if args.seed:
seed_path = Path(args.seed)
with seed_path.open('rb') as seed_file:
seed_data = seed_file.read()
seed = ScrapNode(child=seed_data)
exists = session.query(ScrapNode).filter( exists = session.query(ScrapNode).filter(
ScrapNode.checksum == seed.checksum) ScrapNode.checksum == seed.checksum)
if exists: if exists:
seed = exists[0] seed = exists[0]
else: else:
session.add(seed) session.add(seed)
session.commit() session.commit()
logger.debug(f'args.seed:\n{seed}')
elif recent:
seed = recent
logger.debug(f'recent:\n{seed}')
else: else:
recent = session.query(ScrapNode).order_by(desc('ctime')).first() seed = ScrapNode(child=seed_shell)
session.add(seed)
if recent: session.commit()
logger.debug(f'recent:\n{recent}') logger.debug(f'seed_shell:\n{seed}')
seed = recent
parent = seed parent = seed
queue = Queue() queue = Queue()
while True: while True:
lineage = 0 lineage = 0
scrap = flip(parent.image)
while lineage < args.lineage: while lineage < args.lineage:
scrap = mutate(parent.image)
logger.debug(f'lineage: {lineage}') logger.debug(f'lineage: {lineage}')
result = None result = None
proc = Process(target=generation, args=(queue, scrap)) proc = Process(target=generation, args=(queue, scrap))
proc.start() proc.start()
try: try:
result = queue.get(timeout=1) result = queue.get(timeout=1)
except Empty: except Empty:
@ -110,10 +108,11 @@ def sins():
lineage += 1 lineage += 1
continue continue
scrap = growth(shellcode=scrap, length=result)
parent = ScrapNode(child=scrap, parent_id=parent.id) parent = ScrapNode(child=scrap, parent_id=parent.id)
parent.length = result
session.add(parent) session.add(parent)
session.commit() session.commit()
logger.info(f'scrap:\n{parent}') logger.info(f'scrap:\n{parent}')
lineage = 0 lineage = 0
scrap = flip(parent.image)