Browse Source

scrap growth one nop at a time, store disassembly

master
JoYo 3 years ago
parent
commit
d6f1f12669
  1. 4
      Dockerfile
  2. 2
      sins/__init__.py
  3. 13
      sins/disassemble.py
  4. 35
      sins/mutation.py
  5. 21
      sins/orm.py
  6. 45
      sins/run.py

4
Dockerfile

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

2
sins/__init__.py

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

13
sins/disassemble.py

@ -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

35
sins/mutation.py

@ -4,14 +4,6 @@ from random import randint
import ctypes
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([
b'\x55',
b'\x48\x89\xe5',
@ -22,8 +14,19 @@ seed_shell = b''.join([
b'\x5d',
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)
offset = randint(0, len(shellcode) - 1)
flip = randint(0, 255)
@ -47,3 +50,17 @@ def generation(queue: Queue, shellcode: bytes):
result = function(shellcode_len)
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)

21
sins/orm.py

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

45
sins/run.py

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

Loading…
Cancel
Save