Skip to content

Commit

Permalink
Formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
phutelmyer committed Oct 23, 2023
1 parent aa8e244 commit 5d7b919
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 160 deletions.
54 changes: 28 additions & 26 deletions src/python/strelka/auxiliary/xl4ma/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ def _make_temp_file(data, file_type):
def _get_file_type(data):
file_type = None

if data[:2] == b'\xd0\xcf':
file_type = 'xls'
elif data[:2] == b'\x50\x4b':
file_type = 'xlsx'
if file_type == 'xlsx':
if bytes('workbook.bin', 'ascii') in data:
file_type = 'xlsb'
if bytes('workbook.xml', 'ascii') in data:
file_type = 'xlsm'
if data[:2] == b"\xd0\xcf":
file_type = "xls"
elif data[:2] == b"\x50\x4b":
file_type = "xlsx"
if file_type == "xlsx":
if bytes("workbook.bin", "ascii") in data:
file_type = "xlsb"
if bytes("workbook.xml", "ascii") in data:
file_type = "xlsm"

temp_file = _make_temp_file(data, file_type)

Expand All @@ -42,27 +42,24 @@ def process_data(data, filename):

results = dict()

if file_type == 'xls':
if file_type == "xls":
excel_doc = XLSWrapper(file_path)
elif file_type == 'xlsb':
elif file_type == "xlsb":
excel_doc = XLSBWrapper(file_path)
elif file_type == 'xlsm':
elif file_type == "xlsm":
excel_doc = XLSMWrapper(file_path)

if not hasattr(excel_doc, 'workbook'):
logging.debug('file not supported')
if not hasattr(excel_doc, "workbook"):
logging.debug("file not supported")
return

results.update(excel_doc.parse_sheets(file_path))
results['meta'].update({
'file_name': filename,
'file_type': file_type
})
results["meta"].update({"file_name": filename, "file_type": file_type})

excel_doc_decoded = decode(file_path, file_type, results['defined_names'])
excel_doc_decoded = decode(file_path, file_type, results["defined_names"])

results['decoded'] = excel_doc_decoded
results['iocs'] = iocs(excel_doc_decoded)
results["decoded"] = excel_doc_decoded
results["iocs"] = iocs(excel_doc_decoded)

temp_file.close()
os.unlink(temp_file.name)
Expand All @@ -71,22 +68,27 @@ def process_data(data, filename):


def parse_args(args=None):
parser = argparse.ArgumentParser(
description='Excel4 Macro Analyzer'
parser = argparse.ArgumentParser(description="Excel4 Macro Analyzer")
parser.add_argument(
"--file",
required=True,
type=str,
action="store",
metavar="FILE_PATH",
help="path to file",
)
parser.add_argument('--file', required=True, type=str, action='store', metavar='FILE_PATH', help="path to file")

return parser.parse_args(args)


def main(args=None):
args = parse_args(args)

with open(args.file, 'rb') as fd:
with open(args.file, "rb") as fd:
data = fd.read()

process_data(data, filename=Path(args.file).name)


if __name__ == "__main__":
main()
main()
12 changes: 8 additions & 4 deletions src/python/strelka/auxiliary/xl4ma/extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@
def iocs(excel_doc_decoded):
extracted = set()
for decoded in excel_doc_decoded:
if url := re.findall('(https?://[A-Za-z0-9-._]+/[A-Za-z0-9-._~:/?#\[\]@!$&\'\(\)*+,;%=]+[^,\s\)])', decoded, flags=re.IGNORECASE):
if url := re.findall(
"(https?://[A-Za-z0-9-._]+/[A-Za-z0-9-._~:/?#\[\]@!$&'\(\)*+,;%=]+[^,\s\)])",
decoded,
flags=re.IGNORECASE,
):
scheme, netloc, path, params, query, fragment = urlparse(url[0])
if netloc.startswith('0x'):
if netloc.startswith("0x"):
netloc = socket.inet_ntoa(struct.pack(">L", int(netloc, 16)))
if netloc.startswith('0o'):
if netloc.startswith("0o"):
netloc = socket.inet_ntoa(struct.pack(">L", int(netloc, 8)))
extracted.add(f"{scheme}://{netloc}{path}")

return list(sorted(extracted))
return list(sorted(extracted))
61 changes: 45 additions & 16 deletions src/python/strelka/auxiliary/xl4ma/xl4decoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,20 @@ def _sanitize_results(results):

for result in results or []:
if isinstance(result, str):
sanitized = ", ".join([param.lstrip(' ').rstrip(' ') for param in str(result).replace('"', '').split(',')])
sanitized = ", ".join(
[
param.lstrip(" ").rstrip(" ")
for param in str(result).replace('"', "").split(",")
]
)
sanitized_results.append(sanitized)

return sanitized_results


# XLS
def _decode_xls(file_path, defined_names):
wb = xlrd2.open_workbook(file_path, logfile=open(devnull, 'w'))
wb = xlrd2.open_workbook(file_path, logfile=open(devnull, "w"))
book = Workbook()
for sheet_name in wb.sheet_names():
sheet_xls = wb.sheet_by_name(sheet_name)
Expand All @@ -31,9 +36,15 @@ def _decode_xls(file_path, defined_names):
for col in range(0, sheet_xls.ncols):
try:
if wb[sheet_name].cell(row, col).ctype in (3, 4, 5):
book_sheet.cell(row+1, col+1, f"={wb[sheet_name].cell(row, col).formula}")
book_sheet.cell(
row + 1,
col + 1,
f"={wb[sheet_name].cell(row, col).formula}",
)
elif wb[sheet_name].cell(row, col).ctype in (1, 2):
book_sheet.cell(row+1, col+1, wb[sheet_name].cell(row, col).value)
book_sheet.cell(
row + 1, col + 1, wb[sheet_name].cell(row, col).value
)
except:
pass
temp_file = tempfile.NamedTemporaryFile(suffix=".xlsx", delete=False)
Expand All @@ -54,19 +65,35 @@ def _decode_xlsb(file_path, defined_names):
for cell in row:
if isinstance(cell.value, ErrorValue):
formula = Formula.parse(cell.formula).stringify(wb)
if '(' in formula and ')' in formula:
book_sheet.cell(cell.row.num + 1, cell.col + 1, f"={formula}")
if "(" in formula and ")" in formula:
book_sheet.cell(
cell.row.num + 1, cell.col + 1, f"={formula}"
)
else:
book_sheet.cell(cell.row.num + 1, cell.col + 1, formula)
elif isinstance(cell.formula, bytes) and (isinstance(cell.value, bool)):
book_sheet.cell(cell.row.num + 1, cell.col + 1, f"={Formula.parse(cell.formula).stringify(wb)}")
elif isinstance(cell.formula, bytes) and (
isinstance(cell.value, bool)
):
book_sheet.cell(
cell.row.num + 1,
cell.col + 1,
f"={Formula.parse(cell.formula).stringify(wb)}",
)
elif cell.value:
if isinstance(cell.value, int):
book_sheet.cell(cell.row.num + 1, cell.col + 1, int(cell.value))
book_sheet.cell(
cell.row.num + 1, cell.col + 1, int(cell.value)
)
elif isinstance(cell.value, float):
book_sheet.cell(cell.row.num + 1, cell.col + 1, float(cell.value))
book_sheet.cell(
cell.row.num + 1, cell.col + 1, float(cell.value)
)
elif isinstance(cell.value, str):
book_sheet.cell(cell.row.num + 1, cell.col + 1, str(cell.value).rstrip('\x00'))
book_sheet.cell(
cell.row.num + 1,
cell.col + 1,
str(cell.value).rstrip("\x00"),
)
except:
pass
temp_file = tempfile.NamedTemporaryFile(suffix=".xlsx", delete=False)
Expand All @@ -78,16 +105,18 @@ def _decode_xlsb(file_path, defined_names):

# XLSM
def _decode_xlsm(file_path, defined_names):
with tempfile.NamedTemporaryFile(suffix=f".xlsm", delete=False) as temp_file, open(file_path, 'rb') as fp:
with tempfile.NamedTemporaryFile(suffix=f".xlsm", delete=False) as temp_file, open(
file_path, "rb"
) as fp:
temp_file.write(fp.read())

return _sanitize_results(Interpreter(defined_names).calculate(temp_file))


def decode(file_path, file_type, defined_names):
if file_type == 'xls':
if file_type == "xls":
return _decode_xls(file_path, defined_names)
if file_type == 'xlsb':
if file_type == "xlsb":
return _decode_xlsb(file_path, defined_names)
if file_type == 'xlsm':
return _decode_xlsm(file_path, defined_names)
if file_type == "xlsm":
return _decode_xlsm(file_path, defined_names)
95 changes: 51 additions & 44 deletions src/python/strelka/auxiliary/xl4ma/xl4interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@


class Interpreter:

def __init__(self, defined_names):
self.results = set()
self.defined_names = defined_names
Expand Down Expand Up @@ -39,21 +38,27 @@ def eval_char(x):
def eval_formula(self, args):
if len(args) == 2:
x, y = args
if isinstance(x, formulas.ranges.Ranges) and isinstance(y, formulas.functions.Array):
if isinstance(x, formulas.ranges.Ranges) and isinstance(
y, formulas.functions.Array
):
# y[0][0] = x.value[0][0]
y[0][0] = str(x.value[0][0]).replace('"&"', '')
y[0][0] = str(x.value[0][0]).replace('"&"', "")
self.results.add(y[0][0])
return y[0][0]

if isinstance(x, formulas.functions.Array) and isinstance(y, formulas.ranges.Ranges):
if isinstance(x, formulas.functions.Array) and isinstance(
y, formulas.ranges.Ranges
):
# y.value[0][0] = x[0][0]
y.value[0][0] = str(x[0][0]).replace('"&"', '')
y.value[0][0] = str(x[0][0]).replace('"&"', "")
self.results.add(y.value[0][0])
return y.value[0][0]

if isinstance(x, formulas.ranges.Ranges) and isinstance(y, formulas.ranges.Ranges):
if isinstance(x, formulas.ranges.Ranges) and isinstance(
y, formulas.ranges.Ranges
):
# y.value[0][0] = x.value[0][0]
y.value[0][0] = str(x.value[0][0]).replace('"&"', '')
y.value[0][0] = str(x.value[0][0]).replace('"&"', "")
self.results.add(y.value[0][0])
return y.value[0][0]

Expand All @@ -69,7 +74,7 @@ def eval_formula(self, args):

def eval_set_name(self, args):
name, arg = args
self.results.add(str(arg).replace('^', ''))
self.results.add(str(arg).replace("^", ""))
return arg

def eval_custom(self, args, name=""):
Expand All @@ -83,7 +88,7 @@ def eval_custom(self, args, name=""):
if isinstance(arg.tolist(), str):
func_args.append(arg.tolist())
if isinstance(arg, str):
func_args.append(str(arg).replace('^', ''))
func_args.append(str(arg).replace("^", ""))
if isinstance(arg, int):
func_args.append(str(arg))
if isinstance(arg, float):
Expand All @@ -102,43 +107,45 @@ def eval_run(self, args):

def calculate(self, temp_file):
FUNCTIONS = formulas.get_functions()
FUNCTIONS['ALERT'] = lambda *args: None
FUNCTIONS['ARGUMENT'] = lambda *args: None
FUNCTIONS['BEEP'] = lambda *args: None
FUNCTIONS['_XLFN.BITXOR'] = lambda *args: args[0] ^ args[1]
FUNCTIONS['CALL'] = lambda *args: self.eval_call(args, 'CALL')
FUNCTIONS['CHAR'] = lambda x: self.eval_char(x)
FUNCTIONS['CLOSE'] = lambda *args: None
FUNCTIONS['COUNTBLANK'] = lambda *args: None
FUNCTIONS['DOCUMENTS'] = lambda *args: None
FUNCTIONS['ECHO'] = lambda *args: None
FUNCTIONS['END.IF'] = lambda *args: None
FUNCTIONS['ERROR'] = lambda *args: None
FUNCTIONS['EXEC'] = lambda *args: self.eval_custom(args, 'EXEC')
FUNCTIONS['FORMULA'] = lambda *args: self.eval_formula(args)
FUNCTIONS['FORMULA.ARRAY'] = lambda *args: self.eval_formula(args)
FUNCTIONS['FORMULA.CONVERT'] = lambda *args: None
FUNCTIONS['FORMULA.FILL'] = lambda *args: self.eval_formula(args)
FUNCTIONS['GET.DOCUMENT'] = lambda *args: None
FUNCTIONS['GET.NAME'] = lambda *args: None
FUNCTIONS['GET.WORKSPACE'] = lambda *args: None
FUNCTIONS['HALT'] = lambda *args: None
FUNCTIONS['OPEN'] = lambda *args: None
FUNCTIONS['REGISTER'] = lambda *args: self.eval_custom(args, 'REGISTER')
FUNCTIONS['RESULT'] = lambda *args: None
FUNCTIONS['RETURN'] = lambda *args: None
FUNCTIONS['RUN'] = lambda *args: self.eval_run(args)
FUNCTIONS['SET.NAME'] = lambda *args: self.eval_set_name(args)
FUNCTIONS['SUBSTITUTE'] = lambda *args: None
FUNCTIONS['T'] = lambda x: x
FUNCTIONS['TEXT'] = lambda x, y: x
FUNCTIONS['WAIT'] = lambda *args: None
FUNCTIONS['WINDOW.MINIMIZE'] = lambda *args: None
FUNCTIONS['WORKBOOK.HIDE'] = lambda *args: None
FUNCTIONS["ALERT"] = lambda *args: None
FUNCTIONS["ARGUMENT"] = lambda *args: None
FUNCTIONS["BEEP"] = lambda *args: None
FUNCTIONS["_XLFN.BITXOR"] = lambda *args: args[0] ^ args[1]
FUNCTIONS["CALL"] = lambda *args: self.eval_call(args, "CALL")
FUNCTIONS["CHAR"] = lambda x: self.eval_char(x)
FUNCTIONS["CLOSE"] = lambda *args: None
FUNCTIONS["COUNTBLANK"] = lambda *args: None
FUNCTIONS["DOCUMENTS"] = lambda *args: None
FUNCTIONS["ECHO"] = lambda *args: None
FUNCTIONS["END.IF"] = lambda *args: None
FUNCTIONS["ERROR"] = lambda *args: None
FUNCTIONS["EXEC"] = lambda *args: self.eval_custom(args, "EXEC")
FUNCTIONS["FORMULA"] = lambda *args: self.eval_formula(args)
FUNCTIONS["FORMULA.ARRAY"] = lambda *args: self.eval_formula(args)
FUNCTIONS["FORMULA.CONVERT"] = lambda *args: None
FUNCTIONS["FORMULA.FILL"] = lambda *args: self.eval_formula(args)
FUNCTIONS["GET.DOCUMENT"] = lambda *args: None
FUNCTIONS["GET.NAME"] = lambda *args: None
FUNCTIONS["GET.WORKSPACE"] = lambda *args: None
FUNCTIONS["HALT"] = lambda *args: None
FUNCTIONS["OPEN"] = lambda *args: None
FUNCTIONS["REGISTER"] = lambda *args: self.eval_custom(args, "REGISTER")
FUNCTIONS["RESULT"] = lambda *args: None
FUNCTIONS["RETURN"] = lambda *args: None
FUNCTIONS["RUN"] = lambda *args: self.eval_run(args)
FUNCTIONS["SET.NAME"] = lambda *args: self.eval_set_name(args)
FUNCTIONS["SUBSTITUTE"] = lambda *args: None
FUNCTIONS["T"] = lambda x: x
FUNCTIONS["TEXT"] = lambda x, y: x
FUNCTIONS["WAIT"] = lambda *args: None
FUNCTIONS["WINDOW.MINIMIZE"] = lambda *args: None
FUNCTIONS["WORKBOOK.HIDE"] = lambda *args: None

for name in self.defined_names:
if name.upper() not in FUNCTIONS:
FUNCTIONS[name.upper()] = lambda *args: self.eval_custom(args, name=name)
FUNCTIONS[name.upper()] = lambda *args: self.eval_custom(
args, name=name
)

try:
xl_model = formulas.ExcelModel().loads(temp_file.name).finish()
Expand All @@ -156,4 +163,4 @@ def calculate(self, temp_file):
temp_file.close()
os.unlink(temp_file.name)

return self.results
return self.results
Loading

0 comments on commit 5d7b919

Please sign in to comment.