scrap growth one nop at a time, store disassembly
parent
42c7364589
commit
d6f1f12669
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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)
|
||||||
|
|
21
sins/orm.py
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.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
|
||||||
|
|
45
sins/run.py
45
sins/run.py
|
@ -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()
|
||||||
else:
|
logger.debug(f'args.seed:\n{seed}')
|
||||||
recent = session.query(ScrapNode).order_by(desc('ctime')).first()
|
elif recent:
|
||||||
|
|
||||||
if recent:
|
|
||||||
logger.debug(f'recent:\n{recent}')
|
|
||||||
seed = recent
|
seed = recent
|
||||||
|
logger.debug(f'recent:\n{seed}')
|
||||||
|
else:
|
||||||
|
seed = ScrapNode(child=seed_shell)
|
||||||
|
session.add(seed)
|
||||||
|
session.commit()
|
||||||
|
logger.debug(f'seed_shell:\n{seed}')
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
Loading…
Reference in New Issue