-
Notifications
You must be signed in to change notification settings - Fork 151
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' of https://github.com/kevoreilly/cape
- Loading branch information
Showing
15 changed files
with
414 additions
and
17 deletions.
There are no files selected for viewing
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# Copyright (C) 2010-2015 Cuckoo Foundation, Optiv, Inc. ([email protected]) | ||
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org | ||
# See the file 'docs/LICENSE' for copying permission. | ||
|
||
import os | ||
import shutil | ||
from subprocess import call | ||
from lib.common.abstracts import Package | ||
|
||
class Combo(Package): | ||
"""CAPE Combo analysis package.""" | ||
|
||
def __init__(self, options={}, config=None): | ||
"""@param options: options dict.""" | ||
self.config = config | ||
self.options = options | ||
self.pids = [] | ||
self.options["combo"] = "1" | ||
|
||
def start(self, path): | ||
args = self.options.get("arguments") | ||
appdata = self.options.get("appdata") | ||
runasx86 = self.options.get("runasx86") | ||
|
||
# If the file doesn't have an extension, add .exe | ||
# See CWinApp::SetCurrentHandles(), it will throw | ||
# an exception that will crash the app if it does | ||
# not find an extension on the main exe's filename | ||
if "." not in os.path.basename(path): | ||
new_path = path + ".exe" | ||
os.rename(path, new_path) | ||
path = new_path | ||
|
||
if appdata: | ||
# run the executable from the APPDATA directory, required for some malware | ||
basepath = os.getenv('APPDATA') | ||
newpath = os.path.join(basepath, os.path.basename(path)) | ||
shutil.copy(path, newpath) | ||
path = newpath | ||
if runasx86: | ||
# ignore the return value, user must have CorFlags.exe installed in the guest VM | ||
call(["CorFlags.exe", path, "/32bit+"]) | ||
return self.execute(path, args, path) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
# Copyright (C) 2010-2015 Cuckoo Foundation. | ||
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org | ||
# See the file 'docs/LICENSE' for copying permission. | ||
|
||
import os | ||
import shutil | ||
|
||
from lib.common.abstracts import Package | ||
|
||
class Combo_dll(Package): | ||
"""CAPE Combo DLL analysis package.""" | ||
PATHS = [ | ||
("SystemRoot", "system32", "rundll32.exe"), | ||
] | ||
|
||
def __init__(self, options={}, config=None): | ||
"""@param options: options dict.""" | ||
self.config = config | ||
self.options = options | ||
self.options["combo"] = "1" | ||
|
||
def start(self, path): | ||
rundll32 = self.get_path("rundll32.exe") | ||
function = self.options.get("function", "#1") | ||
arguments = self.options.get("arguments") | ||
dllloader = self.options.get("dllloader") | ||
|
||
# Check file extension. | ||
ext = os.path.splitext(path)[-1].lower() | ||
# If the file doesn't have the proper .dll extension force it | ||
# and rename it. This is needed for rundll32 to execute correctly. | ||
# See ticket #354 for details. | ||
if ext != ".dll": | ||
new_path = path + ".dll" | ||
os.rename(path, new_path) | ||
path = new_path | ||
|
||
args = "{0},{1}".format(path, function) | ||
if arguments: | ||
args += " {0}".format(arguments) | ||
|
||
if dllloader: | ||
newname = os.path.join(os.path.dirname(rundll32), dllloader) | ||
shutil.copy(rundll32, newname) | ||
rundll32 = newname | ||
|
||
return self.execute(rundll32, args, path) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# Copyright (C) 2010-2015 Cuckoo Foundation. | ||
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org | ||
# See the file 'docs/LICENSE' for copying permission. | ||
|
||
import os | ||
import shutil | ||
|
||
from lib.common.abstracts import Package | ||
|
||
class Hancitor_Dll(Package): | ||
"""CAPE Hancitor DLL analysis package.""" | ||
PATHS = [ | ||
("SystemRoot", "system32", "rundll32.exe"), | ||
] | ||
|
||
def start(self, path): | ||
rundll32 = self.get_path("rundll32.exe") | ||
function = self.options.get("function", "#1") | ||
arguments = self.options.get("arguments") | ||
dllloader = self.options.get("dllloader") | ||
self.options["hancitor"] = "1" | ||
|
||
# Check file extension. | ||
ext = os.path.splitext(path)[-1].lower() | ||
# If the file doesn't have the proper .dll extension force it | ||
# and rename it. This is needed for rundll32 to execute correctly. | ||
# See ticket #354 for details. | ||
if ext != ".dll": | ||
new_path = path + ".dll" | ||
os.rename(path, new_path) | ||
path = new_path | ||
|
||
args = "\"{0}\",{1}".format(path, function) | ||
if arguments: | ||
args += " {0}".format(arguments) | ||
|
||
if dllloader: | ||
newname = os.path.join(os.path.dirname(rundll32), dllloader) | ||
shutil.copy(rundll32, newname) | ||
rundll32 = newname | ||
|
||
return self.execute(rundll32, args, path) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
import os | ||
import struct | ||
import binascii | ||
import logging | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
try: | ||
import bson | ||
HAVE_BSON = True | ||
except ImportError: | ||
HAVE_BSON = False | ||
else: | ||
# The BSON module provided by pymongo works through its "BSON" class. | ||
if hasattr(bson, "BSON"): | ||
bson_decode = lambda d: bson.BSON(d).decode() | ||
# The BSON module provided by "pip install bson" works through the | ||
# "loads" function (just like pickle etc.) | ||
elif hasattr(bson, "loads"): | ||
bson_decode = lambda d: bson.loads(d) | ||
else: | ||
HAVE_BSON = False | ||
|
||
class NGram: | ||
def __init__(self, order): | ||
self.order = order | ||
self.buffer = [] | ||
|
||
def add(self, element): | ||
tmp = None | ||
if not element: | ||
return tmp | ||
|
||
if len(self.buffer) == self.order * 2: | ||
tmp = self.buffer.pop(0) | ||
|
||
if type(element) == list: | ||
self.buffer.append(element) | ||
else: | ||
self.buffer.append([element, 1]) | ||
|
||
self.analyse() | ||
return tmp | ||
|
||
def analyse(self): | ||
tmp = [c[0][0] for c in self.buffer] | ||
if tmp[0:self.order] == tmp[self.order:]: | ||
for i in range(self.order): | ||
self.buffer[i][1] += self.buffer[i+self.order][1] | ||
self.buffer = self.buffer[0:self.order] | ||
|
||
class Compressor: | ||
def __init__(self, level): | ||
self.level = level | ||
self.ngrams = [ NGram(i) for i in range(1,level+1) ] | ||
self.final = [] | ||
|
||
def add(self, element): | ||
head, tail = (self.ngrams[0], self.ngrams[1:]) | ||
out = head.add(element) | ||
|
||
for t in tail: | ||
out = t.add(out) | ||
|
||
if out: | ||
self.final.append(out) | ||
|
||
def flush(self): | ||
for i in range(len(self.ngrams)): | ||
current_buffer = self.ngrams[i].buffer | ||
for out in current_buffer: | ||
for u in range(i+1, len(self.ngrams)): | ||
out = self.ngrams[u].add(out) | ||
if out: | ||
self.final.append(out) | ||
|
||
class CuckooBsonCompressor: | ||
def __init__(self): | ||
self.threads = {} | ||
self.callmap = {} | ||
self.head = [] | ||
self.ccounter = 0 | ||
|
||
def __next_message(self): | ||
data = self.fd_in.read(4) | ||
if not data: | ||
return (False, False) | ||
_size = struct.unpack('I', data)[0] | ||
data += self.fd_in.read(_size - 4) | ||
self.raw_data = data | ||
return (data, bson_decode(data)) | ||
|
||
def run(self, file_path): | ||
if not os.path.isfile(file_path) and os.stat(file_path).st_size: | ||
log.warning('File %s does not exists or it is invalid.', file_path) | ||
return False | ||
|
||
self.fd_in = open(file_path, 'rb') | ||
|
||
msg = '---' | ||
while msg: | ||
data, msg = self.__next_message() | ||
|
||
if msg: | ||
mtype = msg.get('type') # message type [debug, new_process, info] | ||
if mtype not in ['debug', 'new_process', 'info']: | ||
_id = msg.get('I', -1) | ||
if not self.category.startswith('__'): | ||
tid = msg.get('T', -1) | ||
time = msg.get('t', 0) | ||
|
||
if tid not in self.threads: | ||
self.threads[tid] = Compressor(100) | ||
|
||
csum = self.checksum(msg) | ||
self.ccounter += 1 | ||
v = (csum, self.ccounter, time) | ||
self.threads[tid].add(v) | ||
|
||
if csum not in self.callmap: | ||
self.callmap[csum] = msg | ||
else: | ||
self.head.append(data) | ||
else: | ||
self.category = msg.get('category', 'None') | ||
self.head.append(data) | ||
|
||
self.fd_in.close() | ||
|
||
return self.flush(file_path) | ||
|
||
def flush(self, file_path): | ||
# This function flushes ngram buffers within compressor and merges | ||
# threads compressed call lists trying preserve original order | ||
|
||
compressed_path = file_path + '.compressed' | ||
if os.path.isfile(compressed_path): | ||
os.remove(compressed_path) | ||
|
||
fd = open(compressed_path, 'wb') | ||
|
||
for d in self.head: | ||
fd.write(d) | ||
|
||
final = [] | ||
for tid, c in self.threads.items(): | ||
c.flush() | ||
for element, repeated in c.final: | ||
data = self.callmap.get(element[0]).copy() | ||
data['r'] += repeated | ||
data['t'] = element[2] | ||
data['order'] = element[1] | ||
final.append(data) | ||
|
||
final.sort(key=lambda x: x['order']) | ||
|
||
if final and os.path.isfile(compressed_path): | ||
for d in final: | ||
d.pop('order') | ||
edata = bson.BSON.encode(d) | ||
fd.write(edata) | ||
|
||
os.rename(file_path, '{}.raw'.format(file_path)) | ||
os.symlink('{}.compressed'.format(file_path), file_path) | ||
else: | ||
return False | ||
|
||
return True | ||
|
||
def checksum(self, msg): | ||
# This function calculates a 4 bytes checksum for each call | ||
# this value is used for identifying a call setup. | ||
|
||
index = msg.get('I', -1) | ||
args = ''.join([ str(c) for c in msg['args'] ]) | ||
content = [ | ||
str(index), # api call | ||
str(msg['T']), # thread id | ||
str(msg['R']), # caller | ||
str(args), # call args | ||
str(self.category), # category | ||
str(msg['P']) # parentcaller | ||
] | ||
content = ''.join(content) | ||
|
||
# Python3 version | ||
# return binascii.crc32(bytes(content, 'utf8')) | ||
return binascii.crc32(content) | ||
|
Oops, something went wrong.