Commit ac752d58 by Thomas Barabosch Committed by Enkelmann

Fix cwe_checker_to_ida, which was broken due to refactoring of logging (#35)

This fixes issues #34
parent 48ce82b2
import logging
import json
RED = 0x6666FF
ORANGE = 0x6699FF
......@@ -25,159 +25,39 @@ colors = {'CWE190': YELLOW,
class CweWarning(object):
def __init__(self, name, plugin_version, warning):
def __init__(self, name, plugin_version, description, addresses):
self.name = name
self.plugin_version = plugin_version
self.warning = warning
self.description = self.__fix_address(description)
self.color = None
self.address = None
self.cwe_number = None
self.address = [self.__fix_address(address) for address in addresses]
self.highlight = True
class CweWarningParser(object):
'''
Parses a CWE warning emitted by the BAP plugin CweChecker
'''
@staticmethod
def _remove_color(s):
'''
Removes 'color' from string
See https://stackoverflow.com/questions/287871/print-in-terminal-with-colors/293633#293633
'''
return s.replace('\x1b[0m', '').strip()
def parse(self, warning):
try:
splitted_line = warning.split('WARN')
cwe_warning = splitted_line[1].replace(
'u32', '').replace('64u', '').replace(':', '')
cwe_name = self._remove_color(cwe_warning.split(')')[0]) + ')'
cwe_name = cwe_name.split('{')[0].strip() + ' ' + cwe_name.split('}')[1].strip()
plugin_version = cwe_warning.split('{')[1].split('}')[0]
cwe_message = ')'.join(cwe_warning.split(')')[1:])
cwe_message = cwe_message.replace('.', '').replace('32u', '')
return CweWarning(cwe_name, plugin_version, cwe_message)
except Exception as e:
logging.error('[CweWarningParser] Error while parsing CWE warning: %s.', str(e))
return None
def __fix_address(address):
return address.replace(':32u', '').replace(':64u', '')
class Parser(object):
def __init__(self, result_path):
self._result_path = result_path
self._parsers = {'CWE125': self.parse_path,
'CWE190': self.parse_cwe190,
'CWE215': self.not_highlighted,
'CWE243': self.not_highlighted,
'CWE248': self.parse_at,
'CWE332': self.not_highlighted,
'CWE367': self.parse_at,
'CWE415': self.parse_path,
'CWE416': self.parse_path,
'CWE426': self.parse_at,
'CWE457': self.parse_cwe457,
'CWE467': self.parse_cwe467,
'CWE476': self.parse_cwe476,
'CWE560': self.parse_cwe560,
'CWE676': self.parse_cwe676,
'CWE782': self.parse_cwe782,
'CWE787': self.parse_path,
}
def _read_in_config(self):
lines = None
with open(self._result_path, 'r') as f:
lines = f.readlines()
if not lines:
print('[Parser] Could not read in file %s' % self._result_path)
raise Exception()
return lines
@staticmethod
def not_highlighted(warning):
warning.highlight = False
return warning
@staticmethod
def parse_at(warning):
warning.address = warning.warning.split('at ')[-1].split()[0].strip()
return warning
@staticmethod
def parse_path(warning):
warning.address = warning.warning.split('->')[-1].strip()
return warning
def _parse_cwe_warnings(j):
result = []
@staticmethod
def parse_cwe190(warning):
if 'multiplication' in warning.warning:
warning.address = warning.warning.split('multiplication ')[1].split()[0]
if 'warnings' in j:
for w in j['warnings']:
cwe_warning = CweWarning(w['name'], w['version'], w['description'], w['addresses'])
if cwe_warning.name in colors:
cwe_warning.color = colors[cwe_warning.name]
else:
warning.address = warning.warning.split('addition ')[1].split()[0]
return warning
@staticmethod
def parse_cwe248(warning):
warning.address = warning.warning.split('at ')[-1].split(':')[0].strip()
return warning
cwe_warning.highlight = False
result.append(cwe_warning)
@staticmethod
def parse_cwe457(warning):
warning.address = warning.warning.split('at ')[-1].split(':')[0].strip()
return warning
@staticmethod
def parse_cwe467(warning):
warning.address = warning.warning.split('at ')[1].split()[0]
return warning
@staticmethod
def parse_cwe476(warning):
warning.address = warning.warning.split('at ')[1].split()[0]
return warning
@staticmethod
def parse_cwe560(warning):
warning.address = warning.warning.split('Function ')[1].split()[0]
return warning
@staticmethod
def parse_cwe676(warning):
warning.address = warning.warning.split('(')[-1].split(')')[0].split(':')[0]
return warning
@staticmethod
def parse_cwe782(warning):
warning.address = warning.warning.split('(')[1].split(')')[0].strip()
return warning
@staticmethod
def _extract_cwe_number(name):
tmp = name.split(']')[0]
return tmp[1:]
return result
def parse(self):
result = []
cwe_parser = CweWarningParser()
lines = self._read_in_config()
for line in lines:
line = line.strip()
if 'WARN' in line:
warning = cwe_parser.parse(line)
cwe_number = self._extract_cwe_number(warning.name)
warning.cwe_number = cwe_number
if cwe_number in self._parsers:
warning = self._parsers[cwe_number](warning)
warning.color = colors[warning.cwe_number]
if warning.address != 'UNKNOWN':
result.append(warning)
else:
print('Warning: %s not supported.' % cwe_number)
return result
with open(self._result_path) as fhandle:
j = json.load(fhandle)
return self._parse_cwe_warnings(j)
import unittest
import json
import CweCheckerParser
class TestCweCheckerParser(unittest.TestCase):
......@@ -6,93 +7,23 @@ class TestCweCheckerParser(unittest.TestCase):
def setUp(self):
self.parser = CweCheckerParser.Parser('RESULT_PATH')
def test_parse_cwe125(self):
cwe125_warning = '2019-07-04 12:59:18.189 WARN : [CWE125] {0.1} (Out-of-bounds Read) 0x6e9 -> 0x6f7 -> 0x707 -> 0x713 -> 0x71f'
cwe_warning = CweCheckerParser.CweWarning('CWE125', '0.1', cwe125_warning)
expect_res = '0x71f'
res = self.parser.parse_path(cwe_warning)
self.assertEqual(res.address, expect_res)
def test_parse_cwe190(self):
cwe190_warning = '2019-07-04 11:13:05.299 WARN : [CWE190] {0.1} (Integer Overflow or Wraparound) Potential overflow due to multiplication 0x6BC:32u (malloc).'
cwe_warning = CweCheckerParser.CweWarning('CWE190', '0.1', cwe190_warning)
expect_res = '0x6BC:32u'
res = self.parser.parse_cwe190(cwe_warning)
self.assertEqual(res.address, expect_res)
def test_parse_cwe248(self):
cwe248_warning = '2019-07-04 11:22:27.579 WARN : [CWE248] {0.1} (Possibly Uncaught Exception) (Exception thrown at 0xC77:64u).'
cwe_warning = CweCheckerParser.CweWarning('CWE248', '0.1', cwe248_warning)
expect_res = '0xC77'
res = self.parser.parse_cwe248(cwe_warning)
self.assertEqual(res.address, expect_res)
def test_parse_cwe415(self):
cwe415_warning = '2019-07-04 12:59:18.189 WARN : [CWE415] {0.1} (Double Free) 0x6e9 -> 0x6f7 -> 0x707 -> 0x713 -> 0x71f'
cwe_warning = CweCheckerParser.CweWarning('CWE415', '0.1', cwe415_warning)
expect_res = '0x71f'
res = self.parser.parse_path(cwe_warning)
self.assertEqual(res.address, expect_res)
def test_parse_cwe416(self):
cwe416_warning = '2019-07-04 13:04:12.542 WARN : [CWE416] {0.1} (Use After Free) 0x6ee -> 0x6fc -> 0x70c -> 0x718 -> 0x72e'
cwe_warning = CweCheckerParser.CweWarning('CWE415', '0.1', cwe416_warning)
expect_res = '0x72e'
res = self.parser.parse_path(cwe_warning)
self.assertEqual(res.address, expect_res)
def test_parse_cwe457(self):
cwe457_warning = '2019-07-04 11:13:05.303 WARN : [CWE457] {0.1} (Use of Uninitialized Variable) Found potentially unitialized stack variable (FP + 0xFFFFFFFC:32u) in function __do_global_dtors_aux at 0x662:32u'
cwe_warning = CweCheckerParser.CweWarning('CWE457', '0.1', cwe457_warning)
expect_res = '0x662'
res = self.parser.parse_cwe457(cwe_warning)
self.assertEqual(res.address, expect_res)
def test_parse_cwe476(self):
cwe476_warning = '2019-07-04 11:13:05.306 WARN : [CWE476] {0.2} (NULL Pointer Dereference) There is no check if the return value is NULL at 0x6BC:32u (@malloc).'
cwe_warning = CweCheckerParser.CweWarning('CWE476', '0.1', cwe476_warning)
expect_res = '0x6BC:32u'
res = self.parser.parse_cwe476(cwe_warning)
self.assertEqual(res.address, expect_res)
def test_parse_cwe560(self):
cwe560_warning = '2019-07-04 10:57:14.599 WARN : [CWE560] {0.1} (Use of umask() with chmod-style Argument) Function 0x5FC:32u calls umask with argument 666'
cwe_warning = CweCheckerParser.CweWarning('CWE560', '0.1', cwe560_warning)
expect_res = '0x5FC:32u'
res = self.parser.parse_cwe560(cwe_warning)
self.assertEqual(res.address, expect_res)
def test_parse_cwe676(self):
cwe676_warning = '2019-07-04 11:13:05.306 WARN : [CWE676] {0.1} (Use of Potentially Dangerous Function) @make_table (0x6F3:32u) -> @memcpy.'
cwe_warning = CweCheckerParser.CweWarning('CWE676', '0.1', cwe676_warning)
expect_res = '0x6F3'
res = self.parser.parse_cwe676(cwe_warning)
self.assertEqual(res.address, expect_res)
def test_parse_cwe787(self):
cwe787_warning = '2019-07-04 12:59:18.189 WARN : [CWE787] {0.1} (Out-of-bounds Write) 0x6e9 -> 0x6f7 -> 0x707 -> 0x713 -> 0x71f'
cwe_warning = CweCheckerParser.CweWarning('CWE787', '0.1', cwe787_warning)
expect_res = '0x71f'
res = self.parser.parse_path(cwe_warning)
self.assertEqual(res.address, expect_res)
def test_parser(self):
input_data = json.loads("""{
"binary": "test/artificial_samples/build/cwe_190_x86_gcc.out",
"time": 1564552342.0,
"warnings": [
{
"name": "CWE190",
"version": "0.1",
"addresses": [ "0x6BC:32u" ],
"symbols": [ "malloc" ],
"other": [],
"description":
"(Integer Overflow or Wraparound) Potential overflow due to multiplication at 0x6BC:32u (malloc)"
}]}""")
expected_res = 'CWE190'
res = self.parser._parse_cwe_warnings(input_data)
self.assertEqual(len(res), 1)
self.assertEqual(res[0].name, expected_res)
......@@ -6,10 +6,11 @@ class IdaGenerator(object):
def generate(self):
script = "import sark\nimport idaapi\n"
for res in self._results:
if res.highlight:
script += "sark.Line(%s).color = %s\n" % (res.address, res.color)
script += "sark.Line(%s).comments.regular = '%s'\n" % (res.address, res.name)
script += "print('[ %s ] %s')\n" % (res.address, res.name)
if res.highlight and res.address:
first_address = res.address[0]
script += "sark.Line(%s).color = %s\n" % (first_address, res.color)
script += "sark.Line(%s).comments.regular = '%s'\n" % (first_address, res.description)
script += "print('[ %s ] %s')\n" % (first_address, res.description)
else:
script += "print('[ GENERAL ] %s')\n" % res.name
script += "print('[ GENERAL ] %s')\n" % res.description
return script
import os
import json
import argparse
from CweCheckerParser import Parser
......@@ -9,7 +11,7 @@ def parse_args():
description='Generates an anotation script for IDA Pro based on CweChecker results.')
parser.add_argument(
'-i', '--cwe_checker_result', type=str, required=True,
help='The path to the output of CweChecker.')
help='The path to the JSON output of CweChecker.')
parser.add_argument(
'-o', '--anotation_script_output', type=str, required=True,
help='The output path of the anotation script.')
......@@ -18,15 +20,35 @@ def parse_args():
def save_generated_script(outpath, generated_script):
with open(outpath, "w") as f:
f.write(generated_script)
with open(outpath, "w") as fhandle:
fhandle.write(generated_script)
def is_valid_json_file(fpath):
try:
with open(fpath) as fhandle:
json.load(fhandle)
return True
except json.JSONDecodeError:
pass
return False
def main():
args = parse_args()
if not os.path.isfile(args.cwe_checker_result):
print('Input file does not exist.')
return 1
if not is_valid_json_file(args.cwe_checker_result):
print('Input file must be formatted as cwe_checker\'s JSON output.')
return 1
results = Parser(args.cwe_checker_result).parse()
generated_script = IdaGenerator(results).generate()
save_generated_script(args.anotation_script_output, generated_script)
print('Done. Now execute generated script %s with IDAPython (alt+F9).'
% args.anotation_script_output)
if __name__ == '__main__':
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment