add harness generator

This commit is contained in:
yonghwi
2021-06-09 20:09:00 -04:00
parent 7b6a601e4d
commit 7ed8209491
17 changed files with 4611 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
*.pyc
*.swp
*.bak
temptrace*
domitrace*
.vscode
# exclude pin binaries
lib/META-INF
lib/pin
lib/pin*zip
# exclude cache directory
cache/
+166
View File
@@ -0,0 +1,166 @@
# Harness Generator
## Library installation (tested with VS2017 and VS2019)
- Install required python packages
```sh
python3 -m pip install -r requirements.txt
```
- Install pintools
```sh
cd {repo}/lib
wget https://software.intel.com/sites/landingpage/pintool/downloads/pin-3.13-98189-g60a6ef199-msvc-windows.zip
unzip pin-3.13-98189-g60a6ef199-msvc-windows.zip
mv pin-3.13-98189-g60a6ef199-msvc-windows pin
rm -f pin-3.13-98189-g60a6ef199-msvc-windows.zip
```
- Copy the tracer source code to pintools
```sh
cd {repo}/lib
cp -r Tracer pin/source/tools
```
- Compile the tracer (with pintools)
- Open Tracer.sln with visual studio
- Open library_trace.cpp and modify `_WINDOWS_H_PATH_` to a proper path (line 2)
- If you are using VS2017, you should modify platform toolset (under Property - General - Platform Toolset)
- Build the solution
- Test
## Collect Dynamic Run Traces & Harness generation
Then we run a program with PIN-based tracer to
- infer (LCA analysis)
### One trace
```sh
# Trace API calls
pin.exe -t source/tools/Tracer/Release/Tracer.dll ^
-logdir "cor1_1" -trace_mode "all" ^
-only_to_target "test.exe" -only_to_lib "test.dll" ^
-- test.exe input1
# Run harness synthesizer on single trace which starts from START_FUNCTION(...)
python3 synthesizer.py harness -t cor1_1/drltrace.PID.log -d cor1_1/memdump -s START_FUNCTION
```
#### Result example
```c++
#include <stdio.h>
...
typedef int (__stdcall *IDP_Init_func_t)(int);
typedef int (__stdcall *IDP_GetPlugInInfo_func_t)(int);
...
void fuzz_me(char* filename){
IDP_Init_func_t IDP_Init_func;
IDP_GetPlugInInfo_func_t IDP_GetPlugInInfo_func;
...
/* Harness function #0 */
int* c0_a0 = (int*) calloc (4096, sizeof(int));
LOAD_FUNC(dlllib, IDP_Init);
int IDP_Init_ret = IDP_Init_func(&c0_a0);
dbg_printf("IDP_Init, ret = %d\n", IDP_Init_ret);
/* Harness function #1 */
int* c1_a0 = (int*) calloc (4096, sizeof(int));
LOAD_FUNC(dlllib, IDP_GetPlugInInfo);
int IDP_GetPlugInInfo_ret = IDP_GetPlugInInfo_func(&c1_a0);
dbg_printf("IDP_GetPlugInInfo, ret = %d\n", IDP_GetPlugInInfo_ret);
...
/* Harness function #66 */
int* c66_a0 = (int*) calloc (4096, sizeof(int));
LOAD_FUNC(dlllib, IDP_CloseImage);
int IDP_CloseImage_ret = IDP_CloseImage_func(&c66_a0);
dbg_printf("IDP_CloseImage, ret = %d\n", IDP_CloseImage_ret);
}
int main(int argc, char ** argv)
{
if (argc < 2) {
printf("Usage %s: <input file>\n", argv[0]);
printf(" e.g., harness.exe input\n");
exit(1);
}
dlllib = LoadLibraryA("%s");
if (dlllib == NULL){
dbg_printf("failed to load library, gle = %d\n", GetLastError());
exit(1);
}
char * filename = argv[1];
fuzz_me(filename);
return 0;
}
```
### One correct + one incorrect
- Run the same tracer with the same input, but with `-logdir "cor1_2"`.
- Then run the same tracer with a different input, but with `-logdir "cor2_1"`.
```sh
# Trace API calls
pin.exe -t source/tools/Tracer/Release/Tracer.dll ^
-logdir "cor1_2" -trace_mode "all" ^
-only_to_target "test.exe" -only_to_lib "test.dll" ^
-- test.exe input1
# Trace API calls
pin.exe -t source/tools/Tracer/Release/Tracer.dll ^
-logdir "cor2_1" -trace_mode "all" ^
-only_to_target "test.exe" -only_to_lib "test.dll" ^
-- test.exe input2
# Run harness synthesizer on single trace which starts from START_FUNCTION(...)
python3 syn-multi.py harness -t ./ -s START_FUNCTION
```
### LCA Analysis
```sh
# Trace API calls
# - from test.exe -> test.dll (API)
# - file-related APIs (CreateFile, ...)
mkdir dom
pin.exe -t source/tools/Tracer/Release/Tracer.dll ^
-logdir "dom" -trace_mode "dominator" ^
-only_to_target "test.exe" -only_to_lib "test.dll" ^
-- test.exe
# Do LCA analysis with API call traces between CreateFileW ~ CloseHandle
python3 dominator.py -s CreateFileW -e CloseHandle -sample "" -t ./dom/drltrace.PID.log -d ./dom/memdump/
```
#### Result example
```
[*] Displaying Most Frequent Address (Dominator candidates)
>> Total unique harness functions: 400
>> Total number of function address identified: 223
>> Total number of candidate address(es): 4
[*] Dominator analysis
>> Bad candidate (called multiple times): 0xc7c925
>> Good candidate (called only once): 0xc579fb, 0xc5857f, 0xc56820
>> Candidate address (sorted by the distance from harness): 0xc579fb, 0xc5857f, 0xc56820
```
### Troubleshooting
- The three synthesizers (synthesizer.py, syn-multi.py, dominator.py) utilizes static analyzer (e.g., IDA Pro).
If it fails to find IDA Pro, try adjusting `IDA_PATH` in harconf.py.
+792
View File
@@ -0,0 +1,792 @@
import subprocess
import typing
import re
import json
import hashlib
import os
import bisect
from harconf import *
from template import *
from util import strings, u32
### PARSER: COMMON
def get_baseaddr(chunk: bytes, modulename: bytes):
lines = chunk.split(b"\n")
for line in lines:
if modulename in line:
return int(line.split(b',')[2], 16)
raise Exception("No modulename in the entry?")
def ret_start_point(pn: str, keyword: bytes):
"""
1) return cid and tid from this example line
CALLID[3] TID[3756] IJ T2M 0x63621040->0x65cf6450(avformat-gp-57.dll!avformat_open_input+0x0)
2) for now, this function is case sensitive
"""
with open(pn, 'rb') as f:
lines = f.readlines()
for line in lines:
if keyword in line and b"0x0" in line:
cid = int(line.split(b"CALLID[")[1].split(b"]")[0])
tid = int(line.split(b"TID[")[1].split(b"]")[0])
return cid, tid
raise Exception("Cannot find the starting function from the trace file")
### PARSER: FUNCTION
class Args:
def __init__(self, name: bytes, addr: int, ret_type: str, args: typing.List[str], convention: str):
"""
e.g., ret : void
e.g., args: ["int", "*int", int]
"""
self.name = name
self.addr = addr
self.ret_type = ret_type
self.args = args
self.convention = convention # std or cdecl
def arg(self, index):
if index < len(self.args):
return self.args[index].replace("*", "")
raise Exception("Index out of bound")
def argtype(self, index):
if index < len(self.args):
arg = self.args[index]
if "*" in arg:
return "data"
else:
return "pointer"
raise Exception("Index out of bound")
@property
def argsize(self):
return len(self.args)
@property
def rettype(self):
return self.ret_type
class Functype:
def __init__(self, functype_pn):
self.functypes_by_name = {}
self.functypes_by_addr = {}
self.functype_pn = functype_pn
self.sorted_addr = []
self.parse()
def parse(self):
with open(self.functype_pn, 'rb') as f:
lines = f.readlines()
for line in lines:
if b"|" not in line:
continue
payload = [x.strip() for x in line.strip().split(b"|", 2)]
if payload[2] == "None":
continue
# e.g., 0x000|aix_read_header|int __cdecl(int)|{"args": [...], "cc": "__stdcall", "ret_type": "..."}
addr = int(payload[0], 16)
funcname = payload[1]
funcinfo = json.loads(payload[2])
ret_type = funcinfo['ret_type']
convention = funcinfo['cc']
args = [arg['type'] for arg in funcinfo['args']]
self.functypes_by_addr[addr] = \
self.functypes_by_name[funcname] = \
Args(funcname, addr, ret_type, args, convention)
self.sorted_addr.append(addr)
self.sorted_addr = sorted(self.sorted_addr)
def by_name(self, funcname) -> Args:
return self.functypes_by_name[funcname]
def by_addr(self, funcaddr) -> Args:
fi = self.functypes_by_addr.get(funcaddr)
if not fi:
return Args(b'', funcaddr, 'int', ['int'] * 9, '__cdecl')
else:
return fi
def by_addr_near(self, funcaddr) -> Args:
index = bisect.bisect_right(self.sorted_addr, funcaddr) - 1
assert index != -1, "Function not found at %s!" % hex(funcaddr)
addr = self.sorted_addr[index]
return self.functypes_by_addr[addr]
### PARSER: TRACE
class TraceElement:
# An argument looks like this: [(0x14334333, "DP"), (0x000000, "D")]
args: typing.List[typing.List[typing.Tuple[int, str]]]
# get dump from bin files
args_dump: typing.List[typing.Tuple[bytes, bytes]]
def __init__(self, tracetype: str, src_addr: int, dst_addr: int, src_module: bytes, dst_module: bytes):
self.tracetype = tracetype # either "call" or "ret"
self.src_addr = src_addr
self.dst_addr = dst_addr # this is also return_address
self.args = []
self.args_dump = []
self.ret_val = None
# Optional symbols
self.has_symbols = bool(dst_module or src_module)
self.src_module = (src_module or '').lower()
self.dst_module = (dst_module or '').lower()
self.src_symbol = None
self.dst_symbol = None
def update_calltrace(self, dst_sym, args, args_dump, pointer):
self.dst_symbol = dst_sym
self.args = args
self.args_dump = args_dump
self.pointer = pointer
def update_rettrace(self, ret_val):
self.ret_val = ret_val
def __repr__(self):
return "TraceElement(%r, %s, %s)" % ((self.tracetype, hex(self.src_addr), hex(self.dst_addr)))
class FunctypeManager:
cache = {}
def __init__(self):
pass
@staticmethod
def get(path) -> Functype:
res = FunctypeManager.cache.get(path)
if res:
return res
cache_out = FunctypeManager.dest_filename(path)
cache_idb = cache_out + '.idb'
command = [IDA_PATH, '-A', '-S' + IDA_SCRIPT]
command += ['-o' + cache_idb, path] if not os.path.isfile(cache_idb) else [cache_idb]
if not os.path.isfile(cache_out):
print(f"Generating function types for {path.decode()} ...")
subprocess.run(command,
env={'TVHEADLESS': '1', 'DESTPATH': cache_out, **os.environ})
else:
print(f"Found cached function types for {path.decode()} !")
if not os.path.isfile(cache_out):
raise Exception(f"Generating function type for {path} failed!\nCommand: {command}")
res = FunctypeManager.cache[path] = Functype(cache_out)
return res
@staticmethod
def dest_filename(path) -> str:
return os.path.join(FUNCTYPE_CACHE_PATH, hashlib.sha256(path.lower()).hexdigest())
class Trace:
# from print_address; address, [module, symbol, offset]. []: optional
CALL_ENTRY = r'({address})(?:\(({module})!({symbol})\+({address})\))?'\
.format(address=r'0x[0-9a-fA-F]+', module=r'[^!]*', symbol=r'[^+]*')\
.encode()
cid_sequence: typing.List[int]
# TODO: parse all traces, now we are tracing specified threadID with starting point
def __init__(self, trace_pn: str, dumpdir: str, tid=None, start_cid=None, build=True):
self.trace_pn = trace_pn
self.functype_manager = FunctypeManager()
self.tid = tid
self.start_cid = start_cid
self.cid_sequence = []
self.dumpdir = dumpdir
if build:
self.build()
#self.module_baseaddr = None
#self.caller_baseasddr = None
"""
print hex(self.calltrace[0].src_addr)
print hex(self.calltrace[0].dst_addr)
print self.calltrace[0].dst_symbol
print self.calltrace[0].args
"""
def build(self):
"""
Analyze trace with function_type information (limit the number of arguments)
"""
calltrace: typing.Dict[int, TraceElement]
rettrace: typing.Dict[int, TraceElement]
calltrace = {}
rettrace = {}
self.modules = modules = {}
with open(self.trace_pn, 'rb') as f:
entries = f.read().split(b"==\n")
entries, module_trace = entries[:-1], entries[-1]
for line in module_trace.strip().split(b'\n')[1:]:
line = map(lambda x: x.strip(), line.split(b',', 9))
idx1, idx2, base, end, entry, a, b, c, path = line
modules[path.lower()] = int(base, 16), int(end, 16)
for x in range(len(entries) - 1):
chunk = entries[x + 1]
tid, cid, tracetype = self.get_tid(chunk)
if self.tid is not None and tid != self.tid:
continue
if tracetype == "CALL":
calltrace[cid] = self.parse_call(chunk, tid, cid)
self.cid_sequence.append(cid)
elif tracetype == "RET":
rettrace[cid] = self.parse_ret(chunk)
# store (for just in case)
first_call = calltrace[self.cid_sequence[0]]
self.module_baseaddr = modules[first_call.dst_module][0]
self.caller_baseaddr = modules[first_call.src_module][0]
# remove if None
calltrace = {k: v for k, v in calltrace.items() if v is not None}
rettrace = {k: v for k, v in rettrace.items() if v is not None}
self.calltrace, self.rettrace = calltrace, rettrace
def parse_arg(self, line: bytes, index, tid, cid):
""" example
-A9: 0x002eeec8[DP] > 0x002eef08[DP] > 0x002eef1c[DP] > 0x002eef3c
"""
out: typing.List[typing.Tuple[int, str]]
chain: typing.List[bytes]
out = []
chain = line.split(b":")[1].split(b">")
for value in chain:
if b"[" in value:
pointer_type = value.split(b"[")[1].split(b"]")[0].decode()
actual_value = int(value.split(b"[")[0], 16)
else:
pointer_type = "D"
actual_value = int(value.strip().split(b" ")[0], 16)
out.append((actual_value, pointer_type))
return out
def find_module(self, address: int) -> typing.Tuple[typing.Union[bytes, None], int, int]:
for path, (base, end) in self.modules.items():
if base <= address <= end:
return path, base, end
return None, 0, 0
def find_function(self, address: int) -> typing.Union[Args, None]:
mod, mod_base, _ = self.find_module(address)
if not mod:
return None
ft = self.functype_manager.get(mod)
fi = ft.by_addr(address - mod_base)
return fi
def parse_call(self, chunk: bytes, tid, cid, parse_args=True) -> TraceElement:
""" example
CALLID[0] TID[3756] IJ T2M 0x63621000->0x65cad480(avformat-gp-57.dll!avformat_get_riff_audio_tags+0x0)
-A0: 0x636350a5[DP] > 0xa382c4a3
...
-A9: 0x002eeec8[DP] > 0x002eef08[DP] > 0x002eef1c[DP] > 0x002eef3c
CALLID[0] TID[3664] IC T2M @0x008f10de(math3.exe!fuzz_me+0xde)->0x735a1100(MathLibrary.dll!test+0x0)
"""
lines = chunk.split(b"\n")
arg_lines = list(filter(lambda x: x.startswith(b' -A'), lines[1:]))
args = []
args_dump = []
pointer = []
src, dst = re.findall(Trace.CALL_ENTRY, lines[0])
src_addr = int(src[0], 16)
dst_addr = int(dst[0], 16)
src_module = src[1]
dst_module = dst[1]
dst_sym = None
numargs = len(arg_lines)
mod, _, _ = self.find_module(src_addr)
if mod:
src_module = mod
mod, _, _ = self.find_module(dst_addr)
if mod:
dst_module = mod
fi = self.find_function(dst_addr)
if fi:
dst_sym = fi.name
numargs = fi.argsize
# XXX: Tracer decides the maximum argument size
numargs = min(numargs, len(arg_lines))
if parse_args:
# parse from A1 to A9 (a0 is ret addr (stack))
for x in range(numargs):
current_arg = self.parse_arg(arg_lines[x], x, tid, cid)
args.append(current_arg)
# FIXME: what if we meet code pointer? should we dump code?
# FIXME: we should consider multi-level, multi-element
if current_arg[0][1] == "DP":
dumpread, same, dump_pointer = self.read_dump(x, tid, cid)
args_dump.append(dumpread)
pointer.append(dump_pointer)
else:
args_dump.append(None)
pointer.append(None)
te = TraceElement("call", src_addr, dst_addr, src_module, dst_module)
te.update_calltrace(dst_sym, args, args_dump, pointer)
return te
def read_dump(self, index, tid, cid):
# filename e.g., t788-c99-a8.post
pre_pn = os.path.join(
self.dumpdir, "t%d-c%d-a%d.pre" % (tid, cid, index))
post_pn = os.path.join(
self.dumpdir, "t%d-c%d-a%d.post" % (tid, cid, index))
with open(pre_pn, 'rb') as f:
pre = f.read(BINREAD)
f.seek(BINREAD)
pre_pointer = f.read(1000)
if not os.path.isfile(post_pn): # unmap?
post_pn = pre_pn
with open(post_pn, 'rb') as f:
post = f.read(BINREAD)
f.seek(BINREAD)
post_pointer = f.read(1000)
return (pre, post), pre == post, (pre_pointer, post_pointer)
def parse_ret(self, chunk: bytes) -> TraceElement:
""" example
RETID[2] TID[3756] RET2T 0x65be6913(avformat-gp-57.dll!av_register_all+0xcb3)->0x63641000(MediaSource.ax!libssh2_session_abstract+0x4b50)
RETVAL: 0x00000001
"""
lines = chunk.split(b"\n")
assert len(lines) >= 2
keyword = lines[0].split(b' TID')[1].split(b' ')[1]
assert keyword in (b'RET2M', b'RET2T', b'RETFR'), 'Unknown keyword: %r' % keyword
src, dst = re.findall(Trace.CALL_ENTRY, lines[0])
src_addr = int(src[0], 16)
dst_addr = int(dst[0], 16)
ret_val = int(chunk.split(b"RETVAL:")[1].split(b'\n')[0], 16)
te = TraceElement("ret", src_addr, dst_addr, src[1], dst[1])
te.update_rettrace(ret_val)
return te
def get_tid(self, chunk: bytes):
tracetype = ""
cid = -1
tid = -1
if b" DC " in chunk or b" IC " in chunk or b" IJ " in chunk or b" FR " in chunk:
tracetype = "CALL"
cid = int(chunk.split(b"[")[1].split(b"]")[0])
elif b"RET" in chunk:
tracetype = "RET"
cid = int(chunk.split(b"[")[1].split(b"]")[0])
if b" TID[" in chunk:
tid = int(chunk.split(b" TID[")[1].split(b"]")[0])
assert(tracetype != "")
# no tid information from the chunk
return tid, cid, tracetype
class SimpleTrace(Trace):
def __init__(self, trace_pn):
self.trace_pn = trace_pn
self.cid_sequence = []
self.unique_call = {}
self.calltrace = self.build()
def build(self):
calltrace = {}
with open(self.trace_pn, 'rb') as f:
fdata = f.read().split(b"==\n")
for x in range(len(fdata) - 1):
chunk = fdata[x + 1]
tid, cid, tracetype = self.get_tid(chunk)
if tracetype == "CALL":
calltrace[cid] = self.parse_call(chunk, tid, cid, parse_args=False)
self.cid_sequence.append(cid)
# remove if None
calltrace = {k: v for k, v in calltrace.items() if v is not None}
return calltrace
# This is the important part; program synthesizer
class Synthesizer:
def __init__(self, trace_pn: str, dump_pn: str, functype_pn: str, start_func=None, sample_name: str = None):
self.dump_pn = dump_pn
self.trace_pn = trace_pn
self.start_func = start_func
self.functype_pn = functype_pn
self.sample_name = sample_name
if self.start_func != None:
self.start_cid, self.trace_tid = ret_start_point(self.trace_pn, self.start_func.encode())
else:
self.start_cid, self.trace_tid = None, None
self.functype_manager = FunctypeManager()
self.trace = Trace(self.trace_pn, self.dump_pn, self.trace_tid, self.start_cid)
self.defined_types, self.defined_funcs = self.typedef()
self.defined_variables = []
self.defined_pointer = {} # {address:variable_name}
self.body = []
self.defined_func = []
self.history = {}
def emit_code(self, out_pn=None):
header = HEADER.replace("{typedef}", '\n'.join(self.defined_types))
fuzzme = FUZZME.replace("{funcdef}", '\n'.join(self.defined_funcs))
fuzzme = fuzzme.replace("{harness}", '\n'.join(self.body))
print(header)
print(fuzzme)
print(MAIN)
def typedef(self):
"""
1) for each trace, we identify unique function
2) we prepare type information (ready to emit)
3) emit to global typedef
e.g., typedef int (__stdcall *avformat_get_riff_audio_tags_func_t)();
4) emit to fuzzme()
e.g., avformat_get_riff_audio_tags_func_t avformat_get_riff_audio_tags_func;
"""
defined_types = []
defined_funcs = []
for cid in self.trace.cid_sequence:
te = self.trace.calltrace[cid]
funcname = te.dst_symbol
mod, mod_base, _ = self.trace.find_module(te.dst_addr)
assert mod
funcinfo = self.functype_manager.get(mod).by_addr(te.dst_addr - mod_base)
args = funcinfo.args
args_str = ', '.join(args) if len(args) > 0 else ''
convention = funcinfo.convention
ret_type = funcinfo.ret_type
_types = self.ret_typedef_func(
funcname, args_str, convention, ret_type)
_funcs = self.ret_defined_func(funcname)
if _types not in defined_types:
defined_types.append(_types)
if _funcs not in defined_funcs:
defined_funcs.append(_funcs)
"""
for x in range(len(defined_types)):
print defined_types[x]
for x in range(len(defined_types)):
print defined_funcs[x]
"""
return defined_types, defined_funcs
def analyze(self):
# 1) infer argument for filepath or input data
# 2) diff pre and post function
# 3) discover used pointer
pass
def dig_userinput(self, cid, args, args_dump, args_ptr):
input_pn = os.path.join(os.path.dirname(self.functype_pn), INPUT1)
if os.path.exists(input_pn):
userinput = open(input_pn, 'rb').read()
else:
return None
# for x in range(len(args)):
# if args[x][0][1] == 'DP':
# print userinput in args_dump[x][0]
def build_body(self, *args):
raise NotImplementedError()
def search_pointer(self, addr, passed_cid=10000, internal_use=False):
"""
utility function to pinpoint the location of address in memory dump
"""
result = []
# for each callid
for cid in self.trace.cid_sequence:
if cid > passed_cid:
continue
variables = [] # defined variables: e.g., int a=0
arguments = [] # used arguments: func(&a)
calltrace = self.trace.calltrace[cid]
args = calltrace.args
args_dump = calltrace.args_dump
args_ptr = calltrace.pointer
for x in range(len(args)):
current_arg = args[x][0][0]
if current_arg == addr:
if not internal_use:
print("[*] Passed argument at [cid:%d] [arg:%dth]" % (cid, x))
else:
result.append(("arg", cid, x))
for x in range(len(args)):
if args[x][0][1] == 'DP':
postdump = args_dump[x][1]
for y in range(0, len(postdump), 4):
if addr == u32(postdump[y:y + 4]):
if not internal_use:
print("[*] Memory dump at [cid:%d] [arg:%dth] [idx:%d]" % (cid, x, y))
else:
result.append(("dump", cid, x, y))
if not internal_use:
return None
else:
return result
def ret_pointer_at_dump(self, cid, arg, idx):
# e.g., *((int*)c3_a0[3]
# FIXME: assuming integer type
return "*((int*)c%d_a%d[%d])" % (cid, arg, idx)
def ret_addr_of_var(self, orig_val):
return "&(%s)" % orig_val
def check_searched_result(self, _result, query):
# query: either "arg" or "dump"
# return oldest one
# print _result, "|", query
if _result is None:
return None
for result in _result:
keyword = result[0]
if keyword == query:
return result
return None
def ret_arg_code(self, cid, args, args_dump, args_type, args_ptr):
"""
- cid: call id
- args: actual argument values
- args_dump: followed result from pointer array[0]=pre, array[1]=post
- args_type: inferred type for each argument
"""
need_to_define = []
arguments = []
pointer_defined_flag = False
# 1) will use raw value (basically)
# 2) if pointer, we define variable and pass the address
# 3) if pointer indicates 0, we allocate heap with 1000 size
for x in range(len(args)):
pointer_defined_flag = False
# data pointer
if args[x][0][1] == 'DP':
# TODO: consider data-type when unpack
# 1) infer filename argument (if the string contains filename information)
first_string = next(strings(args_dump[x][0]))
if self.sample_name.encode() in first_string:
arguments.append("filename")
continue
else:
dumped = hex(u32(args_dump[x][0]))
_type = args_type[x].replace("*", "")
# 1-1) infer chuck of actual sample is used in the function
# TODO
# 2) we allocate heap if pointed value is 0
if dumped == '0x0':
# we always allocate enough space for pointer to zero (could be initialization)
need_to_define.append("%s* c%d_a%d = (%s*) calloc (%d, sizeof(%s));" %
(_type, cid, x, _type, BINREAD, _type))
arguments.append("&c%d_a%d" % (cid, x))
# 3) Check pre-defined pointer
# If there is, we reuse the pointer
else:
# print args[x][0][0]
# Is the address is already referenced from the previous pointer?
# print cid
# print args[x][0][0]
# print self.defined_pointer.keys()
if args[x][0][0] not in self.defined_pointer:
# print hex(args[x][0][0])
result = self.search_pointer(args[x][0][0], cid, internal_use=True)
result_arg = self.check_searched_result(result, "arg")
result_dump = self.check_searched_result(result, "dump")
# print result_arg
# print result_dump
# 3-1) searches for the address from the previous operation
# if there exist address in the dump (e.g., assigned after function call),
# we try to use that (only if the dump exist previously)
if result is not None and result_dump is not None:
_cid = result_dump[1]
_arg = result_dump[2]
_idx = result_dump[3]
ptrname = self.ret_pointer_at_dump(_cid, _arg, _idx)
self.defined_pointer[args[x][0][0]] = ptrname
need_to_define.append('')
arguments.append(ptrname)
continue
# 3-2) what if the address is used by another arguments?
elif False:
# elif result is not None and result_arg is not None:
# print result
self.defined_pointer[args[x][0]
[0]] = "&c%d_a%d" % (cid, x)
need_to_define.append(
"%s c%d_a%d = %s;" % (_type, cid, x, dumped))
# 3-3) if not, we define new one
else:
self.defined_pointer[args[x][0]
[0]] = "&c%d_a%d" % (cid, x)
need_to_define.append(
"%s c%d_a%d = %s;" % (_type, cid, x, dumped))
pointer_defined_flag = True
# if it is pre-defined, we do nothing
else:
need_to_define.append('')
# If we don't have choice, we define new pointer
arguments.append(self.defined_pointer[args[x][0][0]])
# 4) Check whether referenced value (from pointer) is defined as another pointer
# e.g., arg1|A --> 0x1000, arg1|B --> A -> 0x1000
# ==> B = &A (not just raw value of A)
if pointer_defined_flag == True:
# now, we are selecting the referenced value (this is also address)
__result = self.search_pointer(
args[x][1][0], cid, internal_use=True)
# print "pointer", __result
__result_arg = self.check_searched_result(__result, "arg")
# print __result_arg
if __result_arg is not None:
result_cid = __result_arg[1]
result_arg = __result_arg[2]
# history = {cid: (need_to_define, arguments)}
# print "DEBUG", result_cid, result_arg, self.history, self.history[result_cid][1]
previous_argument = self.history[result_cid][1][result_arg]
addr_previous_argument = self.ret_addr_of_var(
previous_argument)
# rollback
del self.defined_pointer[args[x][0][0]]
arguments = arguments[:-1]
need_to_define = need_to_define[:-1]
# append arguments
self.defined_pointer[args[x][1]
[0]] = addr_previous_argument
arguments.append(addr_previous_argument)
need_to_define.append('')
elif args[x][0][1] == 'CP':
""" failed trial
code_pointer = args[x][0][0]
self.defined_pointer[args[x][0][0]] = "&c%d_a%d" % (cid, x)
need_to_define.append("%s c%d_a%d = %s;" % (_type, cid, x, code_pointer))
arguments.append(self.defined_pointer[args[x][0][0]])
"""
# print self.trace.caller_baseasddr
raw_value = args[x][0][0]
_type = args_type[x].replace("*", "")
append_str = " /* Possible code pointer offset: %s */" % hex(
int(raw_value) - self.trace.caller_baseaddr)
# print append_str
# we provide the information about the code pointer
need_to_define.append("")
arguments.append(hex(raw_value) + append_str)
# raw data
elif args[x][0][1] == 'D':
# TODO: consider data-type when unpack
raw_value = args[x][0][0]
_type = args_type[x].replace("*", "")
need_to_define.append("")
arguments.append(hex(raw_value))
return need_to_define, arguments
def ret_typedef_func(self, funcname, args_str, convention, ret_type):
# e.g., typedef int (__stdcall *avformat_get_riff_audio_tags_func_t)();
return "typedef %s (%s *%s_func_t)(%s);" % (ret_type, convention, funcname.decode(), args_str)
def ret_defined_func(self, funcname):
return (b" %s_func_t %s_func;" % (funcname, funcname)).decode()
+426
View File
@@ -0,0 +1,426 @@
# For section IV-B: call-sequence recovery
# From specified targets (leaf nodes; e.g. CreateFile, ReadFile),
# This tries to find a dominator node (or LCA) that will call all of these targets
#!/usr/bin/env python2
import re
import typing
from common import Trace, TraceElement
import os
import signal
import argparse
import networkx as nx
import matplotlib.pyplot as plt
from tqdm import tqdm
from logger import *
from harconf import *
from template import *
from util import exit_gracefully
"""
./dominator.py -t domitrace_alzipcon/trace.log -d domitrace_alzipcon
./dominator.py -t domitrace_alzipcon/trace.log -d domitrace_alzipcon --start Format_QueryCollectionInfo
./dominator.py -t domitrace_alzipcon/trace.log -d domitrace_alzipcon --start Format_QueryCollectionInfo --end Format_Release --sample-name test.egg
alzip: answer is 0x4343d0 (4408272)
"""
logger = getlogger("Dominator")
DUMPDIR = ""
MERGE_BOUNDARY = True
class Dominator(object):
def __init__(self, trace_pn, dump_pn, start_func=None, end_func=None, sample_name=None):
self.dump_pn = dump_pn
self.trace_pn = trace_pn
self.start_func = start_func.encode()
self.end_func = end_func.encode()
self.sample_name = sample_name.encode()
"""
self.trace_tid = -1
self.trace_tid2 = -1
if self.start_func != None:
self.start_cid, self.trace_tid = self.ret_start_point(self.trace_pn, self.start_func)
if self.end_func != None:
self.end_cid, self.trace_tid2 = self.ret_end_point(self.trace_pn, self.end_func)
assert(self.trace_tid == self.trace_tid2)
"""
self.start_cid, self.end_cid, self.interesting_tid, _ = self.ret_interesting_locations()
self.trace = DominatorTrace(self.trace_pn, self.start_cid,
self.end_cid, self.interesting_tid)
self.defined_variables = []
self.defined_pointer = {} # {address:variable_name}
self.body = []
self.history = {}
self.har_addr = {}
""" ALL functions which used in the harness """
for addr in list(self.trace.all_callers.keys()):
# print hex(addr), self.trace.all_callers[addr]
self.har_addr[addr] = self.trace.all_callers[addr]
self.dominator()
# print self.trace.callgraph.edges
# print self.trace.func_boundary
def dominator(self):
# Calculate dominator using DFS and common address (Lowest common ancestor)
unique_code_size = len(self.trace.node_list)
all_nodes = self.trace.callgraph.nodes
print("[*] Processing Directed-graph to find dominator")
storage = []
for harness_addr in tqdm(list(self.trace.all_callers)):
harness_addr_start = self.trace.get_func_start(harness_addr, merge_boundary=MERGE_BOUNDARY)
out = []
for i in range(unique_code_size - 1, 0, -1):
# print(harness_addr, self.trace.node_list[i])
if self.trace.node_list[i] == harness_addr_start or self.trace.node_list[i] == 0:
continue
if self.trace.node_list[i] not in all_nodes:
continue
# print harness_addr_start, self.trace.node_list[i]
for path in nx.all_simple_paths(self.trace.callgraph, source=self.trace.node_list[i], target=harness_addr_start, cutoff=50):
out = out + path
out = list(dict.fromkeys(out))
# print out
storage = storage + out
# Print out the report
print("[*] Displaying Most Frequent Address (Dominator candidates)")
func_counter: typing.Dict[int, int] = {}
for func_addr in storage:
func_counter[func_addr] = func_counter.get(func_addr, 0) + 1
popular_words = sorted(func_counter, key=func_counter.get, reverse=True)
most_func_count = func_counter[popular_words[0]]
report_addr = []
for addr in func_counter.keys():
if func_counter[addr] == most_func_count:
report_addr.append(addr)
print(" >> Total unique harness functions: %d" % (len(self.trace.all_callers)))
print(" >> Total number of function address identified: %d" % most_func_count)
print(" >> Total number of candidate address(es): %d" % len(report_addr))
# print(" >> Total number of candidate address(es): %d" % ', '.join(report_addr))
# for debug
# report_addr = ['0x421000', '0x430200', '0x4333c0', '0x424410', '0x41a590', '0x424e20', '0x44d0b0', '0x4477d0', '0x421440', '0x424230', '0x4335c0', '0x433de0', '0x424a10', '0x430e40', '0x452a60', '0x4243f0', '0x44b8c0', '0x447850', '0x4331a0', '0x40fdd0', '0x436860', '0x430610', '0x41ac80', '0x433340', '0x452e70', '0x4339c0', '0x430880', '0x4242c0', '0x4343d0', '0x44ccf0', '0x40fc90', '0x44d200', '0x44baa0', '0x42eea0', '0x41a570', '0x4346b0', '0x4160c0', '0x432920', '0x4330d0', '0x4260d1', '0x423d90', '0x420ac0', '0x424ac0', '0x4308e0', '0x4179d0', '0x423f90', '0x4483a0', '0x424cf0', '0x423360', '0x42e720', '0x457500', '0x447480', '0x417f80', '0x422000', '0x422920', '0x430710', '0x41a610', '0x44d2c0', '0x44d920', '0x433530', '0x422c20', '0x424890', '0x420f30', '0x430680', '0x421ee0', '0x452b10', '0x41ed40', '0x448ae0', '0x421370', '0x424330', '0x447120', '0x430260', '0x44d6b0', '0x430d60', '0x422c90', '0x424c80', '0x422d00', '0x42eb70', '0x422e00', '0x408e80', '0x4326e0', '0x40e180', '0x447940', '0x430f80', '0x422b90', '0x4329e0', '0x44bf90', '0x446fa0', '0x424af0', '0x406ce0', '0x423910', '0x44d9b0', '0x433b10', '0x434330', '0x4231c0', '0x4211a0', '0x433800', '0x44c020', '0x41f0e0', '0x44dbd0', '0x4129c0', '0x44be10', '0x420a40', '0x44e2a0', '0x430de0', '0x418050', '0x434600', '0x415df0', '0x424e40']
# Heuristics
"""
1. display report address with CID (CALLID)
2. display observed number of that address in the trace
(less number is desirable, should be 1?)
3. display upper address of that function
"""
print("\n[*] Dominator analysis")
candidate = {}
candidate["good"] = []
candidate["bad"] = []
for addr in report_addr:
count = self.ret_addr_count_trace(addr) # how many times called?
if count == 1:
candidate["good"].append(addr)
else:
candidate["bad"].append(addr)
final_report = {}
for good_addr in candidate["good"]:
final_report[good_addr] = self.distance_from_startcid(good_addr)
final_report = sorted(final_report, key=final_report.get, reverse=False)
print(" >> Bad candidate (called multiple times): %s" % ', '.join(hex(addr) for addr in candidate["bad"]))
print(" >> Good candidate (called only once): %s" % ', '.join(hex(addr) for addr in candidate["good"]))
print(" >> Candidate address (sorted by the distance from harness): %s" % ', '.join(hex(addr) for addr in final_report))
""" how to find this address?
[*] Dominator analysis
{'0x40fc90': 3618, '0x44b8c0': 3493, '0x41a610': 3737, '0x447940': 3242, '0x4335c0': 3615, '0x42eea0': 3504, '0x447120': 3616, '0x446fa0': 3606, '0x452e70': 3601, '0x40e180': 3746, '0x432920': 3604, '0x4346b0': 3168}
>> Candidate address (sorted by the distance from harness):
['0x4346b0', '0x447940', '0x44b8c0', '0x42eea0', '0x452e70', '0x432920', '0x446fa0', '0x4335c0', '0x447120', '0x40fc90', '0x41a610', '0x40e180']
(answer)
4346b0 <- 41a610 <- 40e180
41a610 <- 40fc90
"""
def distance_from_startcid(self, good_addr):
current_cid = 0
for cid in list(self.trace.calltrace.keys()):
if good_addr == self.trace.calltrace[cid].dst_addr:
current_cid = cid
return self.start_cid - current_cid
def ret_addr_count_trace(self, dst_addr):
counter = 0
for cid in list(self.trace.calltrace.keys()):
if dst_addr == self.trace.calltrace[cid].dst_addr:
if self.start_cid > cid:
counter += 1
else:
counter += 2
return counter
def ret_interesting_locations(self):
start_cid = None
start_tid = None
end_cid = 9e999
end_tid = -1
node_out = []
with open(self.trace_pn, 'rb') as f:
lines = f.readlines()
for line in lines:
if b" DC " in line or b" IC " in line or b" IJ " in line or b" FR " in line:
src_addr = int(line.split(b"->")[0].split(b"(")[0].split(b"0x")[1], 16)
# dst_addr = int(line.split(b"->")[1].split(b"(")[0], 16)
if src_addr not in node_out:
node_out.append(src_addr)
if self.start_func in line and b"0x0" in line:
cid = int(line.split(b"CALLID[")[1].split(b"]")[0])
tid = int(line.split(b"TID[")[1].split(b"]")[0])
if start_cid == None:
start_cid = cid
start_tid = tid
if self.end_func in line and b"0x0" in line:
cid = int(line.split(b"CALLID[")[1].split(b"]")[0])
tid = int(line.split(b"TID[")[1].split(b"]")[0])
end_cid = cid
end_tid = tid
return start_cid, end_cid, start_tid, node_out
class DominatorTrace(Trace):
# TODO: parse all traces, now we are tracing specified threadID with starting point
def __init__(self, trace_pn, start_cid, end_cid, interesting_tid):
super().__init__(trace_pn, DUMPDIR, interesting_tid, start_cid, build=False)
self.func_boundary = {}
self.src_to_dst = {}
self.dst_from_src = {}
self.start_cid = start_cid
self.end_cid = end_cid
self.all_callers = {}
self.node_list = []
self.callgraph = nx.DiGraph()
self.possible_return = {}
self.build()
# test
# print(self.calltrace[0])
# print(self.rettrace[1])
self.func_boundary = self.sanitize_func_boundary() # dict{start:dst}
self.generate_digraph() # store to self.callgraph
# self.show_graph()
# print self.func_boundary
""" TEST function boundary (start ==> dst)
for key in self.func_boundary.keys():
print(hex(key), hex(self.func_boundary[key])) # src->dst
"""
""" TEST whether same source has multiple targets (i.e., indirect call)
for key in self.src_to_dst.keys():
print(hex(key), self.src_to_dst[key])
"""
""" TEST Xref functions (i.e., who are calling this function?)
for key in self.dst_from_src.keys():
print(hex(key), self.dst_from_src[key])
"""
def generate_digraph(self):
for cid in list(self.calltrace.keys()):
te = self.calltrace[cid]
start = te.src_addr
end = te.dst_addr
func_start = self.get_func_start(start, merge_boundary=MERGE_BOUNDARY)
# if func_start == 0 and src_addr == 4303892:
# print "here", src_addr, dst_addr
if func_start not in self.node_list:
self.node_list.append(func_start)
# print src_addr, func_start
self.callgraph.add_edge(func_start, end)
# print(f"Call {hex(start)}: {hex(func_start)} -> {hex(end)}")
def show_graph(self):
nx.draw(self.callgraph, node_size=200)
plt.show()
def get_func_start(self, addr, merge_boundary=False):
# for start_addr in self.func_boundary.keys():
# if start_addr <= addr <= self.func_boundary[start_addr]:
# return start_addr
# # one more try to find and change the function boundary
# """
# Before>
# A ---- B C-----D
# E
# After>
# A ---------E C-----D
# option1: just return A
# option1: return A and modify the function boundary
# """
# if merge_boundary:
# # find cloest boundary
# diff = 0x100000
# candidate = 0
# for start_addr in self.func_boundary.keys():
# end_addr = self.func_boundary[start_addr]
# if end_addr < addr < end_addr + diff:
# candidate = start_addr
# diff = addr - end_addr
# if candidate != 0:
# return candidate
# Addition: use function type database
mod, mod_base, _ = self.find_module(addr)
if mod:
ft = self.functype_manager.get(mod)
fi = ft.by_addr_near(addr - mod_base)
return fi.addr + mod_base
# finally we give up
return 0
def sanitize_func_boundary(self):
out = {}
for key in list(self.func_boundary.keys()):
start_addr = key
end_addr = self.func_boundary[key]
# print start_addr, end_addr
if end_addr == -1:
continue
if end_addr - start_addr > 0x10000:
continue
out[start_addr] = end_addr
return out
def insert_relationship(self, src, dst):
if src not in list(self.src_to_dst.keys()):
self.src_to_dst[src] = []
self.src_to_dst[src].append(dst)
else:
if dst not in self.src_to_dst[src]:
self.src_to_dst[src].append(dst)
if dst not in list(self.dst_from_src.keys()):
self.dst_from_src[dst] = []
self.dst_from_src[dst].append(src)
else:
if src not in self.dst_from_src[dst]:
self.dst_from_src[dst].append(src)
def windows_target(self, te: TraceElement):
dlls = [b"kernelbase.dll", b"kernel32.dll", b"ntdll.dll"]
if te.dst_module and te.dst_module not in dlls:
return False
return True
def parse_call(self, chunk: bytes, tid, cid):
te = super().parse_call(chunk, tid, cid)
src_addr = te.src_addr
dst_addr = te.dst_addr
# for finding fucntion boundary
self.possible_return[src_addr] = dst_addr
if dst_addr not in self.func_boundary:
self.func_boundary[dst_addr] = -1
# for making src_to_dst and dst_from_src relationship
self.insert_relationship(src_addr, dst_addr)
if cid >= self.start_cid and cid <= self.end_cid:
if te.has_symbols and src_addr not in self.all_callers and not self.windows_target(te):
self.all_callers[src_addr] = te.src_symbol
return te
def parse_ret(self, chunk: bytes):
te = super().parse_ret(chunk)
# update for building function boundary
self.update_ret_addr(te.dst_addr, te.src_addr)
return te
# dst_addr is returning address (returining target)
def update_ret_addr(self, dst_addr, current_addr):
for addr in self.possible_return.keys():
# found return place
if addr <= dst_addr <= addr + 7:
start_addr = self.possible_return[addr]
self.func_boundary[start_addr] = max(
self.func_boundary[start_addr], current_addr)
def main():
global DUMPDIR
# DEFINE PARSER
parser = argparse.ArgumentParser()
parser.add_argument("-t", "--trace", dest="trace_file", type=str,
default=None, help="Trace file collected from DynamoRIO",
required=True)
parser.add_argument("-d", "--memory-dump", dest="dump_dir", type=str,
default=None, help="memory dump file directory (pre/post)",
required=True)
parser.add_argument("-s", "--start", dest="start_func", type=str,
default=None, help="name of the starting function to process",
required=False)
parser.add_argument("-e", "--end", dest="end_func", type=str,
default=None, help="name of the ending function to process",
required=False)
parser.add_argument("-sample", "--sample-name", dest="sample_name", type=str,
default=None, help="name of the original sample name",
required=False)
args = parser.parse_args()
# END PARSER
# Ctrl-c handler
signal.signal(signal.SIGINT, exit_gracefully(
signal.getsignal(signal.SIGINT)))
# Start harness dominator
DUMPDIR = args.dump_dir
domi = Dominator(args.trace_file, args.dump_dir,
args.start_func, args.end_func, args.sample_name)
if __name__ == '__main__':
main()
+35
View File
@@ -0,0 +1,35 @@
import glob
import os
# for functype manager; it invokes an IDAPython script
# TODO: edit this if it doesn't work
ROOT = os.path.dirname(__file__)
IDA_PATH = glob.glob(r'C:\Program Files\IDA *\idat.exe')[0]
IDA_SCRIPT = os.path.join(ROOT, 'util/ida_func_type.py')
FUNCTYPE_CACHE_PATH = os.path.join(ROOT, 'cache')
if not os.path.isdir(FUNCTYPE_CACHE_PATH):
os.mkdir(FUNCTYPE_CACHE_PATH)
if not os.path.exists(IDA_SCRIPT):
print("Check if the following file exists:", IDA_SCRIPT)
exit(1)
# for harnesor
TRACE_PN = "trace"
FUNCTYPE = "functype"
NORMAL_POSTFIX = "normal"
TRACE_PREFIX = ["DC", "RET", "IC", "IJ"]
THRESHOLD = 0x20
TRACE_MAX = 20
INPUT = "QQQQ"
NAMEDIC = {}
NAMEDIC['CP'] = "Code Pointer"
NAMEDIC['DP'] = "Data Pointer"
# for synthesizer
BINREAD = 0x1000
POINTER_SEARCH_LIMIT = 100
INPUT_COMP_LENGTH = 10
+200
View File
@@ -0,0 +1,200 @@
#!/usr/bin/env python3
import os
import glob
from harconf import *
class Tracer(object):
def __init__(self, project):
self.project = project
self.dir = os.path.join(TRACE_PN, self.project)
self.filelist = glob.glob(self.dir+"/*.log")
self.normalfile = self.find_normal_file() # store normal (no interesting points)
self.filelist.remove(self.normalfile)
self.trace_normal = None
self.trace_interesting = {}
self.trace_interesting_line = {}
self.trace_interesting_line2index = {}
self.list_diff = {}
self.funcline_diff = {}
self.modulename = None
def find_normal_file(self):
for filename in self.filelist:
if "_"+NORMAL_POSTFIX in filename:
return filename
raise Exception("No normal file which contains _normal")
@property
def files(self):
return self.filelist
def collect_trace(self):
self.unique_functions_normal, self.modulename, self.unique_functions_line, _ \
= parse_trace_unique_callee(self.normalfile)
for filename in self.filelist:
#print (filename)
basename = os.path.basename(filename)
self.trace_interesting[basename], _, self.trace_interesting_line[basename], \
self.trace_interesting_line2index[basename] = parse_trace_unique_callee(filename)
def extract_unique_callsite(self):
for filename in self.filelist:
#print (list_diff(self.trace_interesting[os.path.basename(filename)], self.unique_functions_normal))
basename = os.path.basename(filename)
self.list_diff[basename] = list_diff(self.trace_interesting[basename], self.unique_functions_normal)
self.funcline_diff[basename] = list_diff(self.trace_interesting_line[basename], self.unique_functions_line)
def print_unique_trace(self):
# print-out the first line of unique call (indirect/direct/ind-jmp)
for key in self.list_diff.keys():
print('\n' + key)
for x in range(len(self.funcline_diff[key])):
print(self.funcline_diff[key][x], self.trace_interesting_line2index[key][self.funcline_diff[key][x]])
def extract_interesting_trace(self, outdir):
for key in self.list_diff.keys():
#print ('\n' + key)
idx_list = []
for x in range(len(self.funcline_diff[key])):
idx = self.trace_interesting_line2index[key][self.funcline_diff[key][x]]
idx_list.append(idx)
min_val, max_val = extract_minmax(idx_list)
out_pn = os.path.join(outdir, key)
dump_extracted_trace(min_val, max_val, out_pn, self.project)
def extract_minmax(idx_list):
min_val = min(idx_list)
max_val = max(idx_list)
while True:
if max_val - min_val > TRACE_MAX:
idx_list.remove(max_val)
max_val = max(idx_list)
else:
break
return min_val, max_val
def list_diff(li1, li2):
return (list(set(li1) - set(li2)))
def get_module_name(chunk):
lines = chunk.split("\n")
if "T2M" in chunk:
return lines[1].split(".dll")[0].split(" ")[-1]+".dll"
elif "M2T" in chunk:
return lines[0].split(".dll")[0].split(" ")[-1]+".dll"
def get_baseaddr(chunk, modulename):
lines = chunk.split("\n")
for line in lines:
if modulename in line:
return int(line.split(',')[2], 16)
raise Exception("No modulename in the entry?")
def sanitize_fcall_line(line, baseaddr):
addr = line.split("0x")[1].split(" ")[0]
newaddr = hex(int(addr, 16) - baseaddr)[2:]
if " @ " in line:
line = line.split(" @ ")[1].strip()
if "=> " in line:
line = line.split("=> ")[1].strip()
line = line.replace(" ? ??:0", "")
line = line.replace(" ??:0", "")
line = line.replace(" to ", "")
line = line.replace(addr, newaddr)
return line.strip()
def extract_call_addr(chunk, baseaddr):
lines = chunk.split("\n")
if lines[0].split(" ")[0].strip() in TRACE_PREFIX:
if lines[0].split(" ")[1].strip() == "T2M":
return int(lines[1].split("0x")[1].split(" ")[0], 16) - baseaddr, sanitize_fcall_line(lines[1], baseaddr)
elif lines[0].split(" ")[1].strip() == "M2T":
return int(lines[0].split("0x")[1].split(" ")[0], 16) - baseaddr, sanitize_fcall_line(lines[0], baseaddr)
#print (chunk)
raise Exception("No keyword? T2M or M2T?")
else:
return None, None
def dump_extracted_trace(min_val, max_val, out_pn, project):
trace_pn = os.path.join(TRACE_PN, project, os.path.basename(out_pn))
#print (trace_pn, filename)
fdata = None
out = ""
with open(trace_pn, 'r') as f:
fdata = f.read().split("==\n")
modulename = get_module_name(fdata[1])
baseaddr = get_baseaddr(fdata[-1], modulename)
for x in range(min_val, max_val+1):
chunk = fdata[x+1]
out += chunk + "==\n"
out += modulename + "|" + hex(baseaddr)
with open(out_pn, 'w') as f:
f.write(out)
def parse_trace_unique_callee(filename):
unique_targets = []
unique_funcline = []
line2index = {}
fdata = None
with open(filename, 'r') as f:
fdata = f.read().split("==\n")
modulename = get_module_name(fdata[1])
baseaddr = get_baseaddr(fdata[-1], modulename)
for x in range(len(fdata)-1):
chunk = fdata[x+1]
fcall_addr, relavant_line = extract_call_addr(chunk, baseaddr)
if fcall_addr is not None:
line2index[relavant_line.strip()] = x
if fcall_addr not in unique_targets:
unique_targets.append(fcall_addr)
unique_funcline.append(relavant_line)
"""
if x not in line2index.keys():
line2index[relavant_line.strip()] = x
"""
return unique_targets, modulename, unique_funcline, line2index
def main():
tr = Tracer("notepad++")
#tr = Tracer("aimp")
tr.collect_trace()
tr.extract_unique_callsite()
# tr.print_unique_trace()
tr.extract_interesting_trace("trace/extracted")
if __name__ == '__main__':
main()
+385
View File
@@ -0,0 +1,385 @@
pin
pin2
ndk
# Created by https://www.gitignore.io/api/c++,visualstudio
# Edit at https://www.gitignore.io/?templates=c++,visualstudio
### C++ ###
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
### VisualStudio ###
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.iobj
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true
**/wwwroot/lib/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# End of https://www.gitignore.io/api/c++,visualstudio
+31
View File
@@ -0,0 +1,31 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29306.81
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Tracer", "Tracer.vcxproj", "{639EF517-FCFC-408E-9500-71F0DC0458DB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{639EF517-FCFC-408E-9500-71F0DC0458DB}.Debug|x64.ActiveCfg = Debug|x64
{639EF517-FCFC-408E-9500-71F0DC0458DB}.Debug|x64.Build.0 = Debug|x64
{639EF517-FCFC-408E-9500-71F0DC0458DB}.Debug|x86.ActiveCfg = Debug|Win32
{639EF517-FCFC-408E-9500-71F0DC0458DB}.Debug|x86.Build.0 = Debug|Win32
{639EF517-FCFC-408E-9500-71F0DC0458DB}.Release|x64.ActiveCfg = Release|x64
{639EF517-FCFC-408E-9500-71F0DC0458DB}.Release|x64.Build.0 = Release|x64
{639EF517-FCFC-408E-9500-71F0DC0458DB}.Release|x86.ActiveCfg = Release|Win32
{639EF517-FCFC-408E-9500-71F0DC0458DB}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {07CB9EAD-D3D1-4D13-96E3-68BBB515F7B7}
EndGlobalSection
EndGlobal
+265
View File
@@ -0,0 +1,265 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{639EF517-FCFC-408E-9500-71F0DC0458DB}</ProjectGuid>
<RootNamespace>MyPinTool</RootNamespace>
<Keyword>Win32Proj</Keyword>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<CharacterSet>MultiByte</CharacterSet>
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<CharacterSet>MultiByte</CharacterSet>
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>10.0.40219.1</_ProjectFileVersion>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectDir)$(Configuration)\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</IntDir>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</LinkIncremental>
<GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectDir)$(Platform)\$(Configuration)\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(Platform)\$(Configuration)\</IntDir>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</LinkIncremental>
<GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</GenerateManifest>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectDir)$(Configuration)\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</IntDir>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
<GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectDir)$(Platform)\$(Configuration)\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(Platform)\$(Configuration)\</IntDir>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental>
<GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</GenerateManifest>
<CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
<CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
<CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
<CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
<CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
<CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
<CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
<CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<AdditionalOptions>/GR- /GS- /EHs- /EHa- /FP:strict /Oi- /FIinclude/msvc_compat.h %(AdditionalOptions)</AdditionalOptions>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\..\include\pin;..\..\include\pin\gen;..\InstLib;..\..\..\extras\xed-ia32\include\xed;..\..\..\extras\components\include;..\..\..\extras\stlport\include;..\..\..\extras;..\..\..\extras\libstdc++\include;..\..\..\extras\crt\include;..\..\..\extras\crt;..\..\..\extras\crt\include\arch-x86;..\..\..\extras\crt\include\kernel\uapi;..\..\..\extras\crt\include\kernel\uapi\asm-x86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>TARGET_IA32;HOST_IA32;TARGET_WINDOWS;WIN32;__PIN__=1;PIN_CRT=1;__i386__</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>
</ExceptionHandling>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<BufferSecurityCheck>false</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<DisableSpecificWarnings>4530;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<Link>
<AdditionalOptions>/export:main /ignore:4210 %(AdditionalOptions)</AdditionalOptions>
<AdditionalDependencies>pin.lib;xed.lib;pinvm.lib;kernel32.lib;pincrt.lib;ntdll-32.lib;crtbeginS.obj</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\..\ia32\lib;..\..\..\ia32\lib-ext;..\..\..\extras\xed-ia32\lib;..\..\..\ia32\runtime\pincrt;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>NotSet</SubSystem>
<OptimizeReferences>false</OptimizeReferences>
<EntryPointSymbol>Ptrace_DllMainCRTStartup%4012</EntryPointSymbol>
<BaseAddress>0x55000000</BaseAddress>
<TargetMachine>MachineX86</TargetMachine>
<AllowIsolation>true</AllowIsolation>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
</Midl>
<ClCompile>
<AdditionalOptions>/GR- /GS- /EHs- /EHa- /FP:strict /Oi- /FIinclude/msvc_compat.h %(AdditionalOptions)</AdditionalOptions>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\..\include\pin;..\..\include\pin\gen;..\InstLib;..\..\..\extras\xed-intel64\include\xed;..\..\..\extras\components\include;..\..\..\extras\stlport\include;..\..\..\extras;..\..\..\extras\libstdc++\include;..\..\..\extras\crt\include;..\..\..\extras\crt;..\..\..\extras\crt\include\arch-x86_64;..\..\..\extras\crt\include\kernel\uapi;..\..\..\extras\crt\include\kernel\uapi\asm-x86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>TARGET_IA32E;HOST_IA32E;TARGET_WINDOWS;WIN32;__PIN__=1;PIN_CRT=1;__LP64__</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>
</ExceptionHandling>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<BufferSecurityCheck>false</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<DisableSpecificWarnings>4530;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<Link>
<AdditionalOptions>/export:main %(AdditionalOptions)</AdditionalOptions>
<AdditionalDependencies>pin.lib;xed.lib;pinvm.lib;kernel32.lib;pincrt.lib;ntdll-64.lib;crtbeginS.obj</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\..\intel64\lib;..\..\..\intel64\lib-ext;..\..\..\extras\xed-intel64\lib;..\..\..\intel64\runtime\pincrt;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>NotSet</SubSystem>
<OptimizeReferences>false</OptimizeReferences>
<EntryPointSymbol>Ptrace_DllMainCRTStartup</EntryPointSymbol>
<BaseAddress>0xC5000000</BaseAddress>
<TargetMachine>MachineX64</TargetMachine>
<AllowIsolation>true</AllowIsolation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<AdditionalOptions>/GR- /GS- /EHs- /EHa- /FP:strict /Oi- /FIinclude/msvc_compat.h %(AdditionalOptions)</AdditionalOptions>
<IntrinsicFunctions>false</IntrinsicFunctions>
<WholeProgramOptimization>false</WholeProgramOptimization>
<AdditionalIncludeDirectories>..\..\include\pin;..\..\include\pin\gen;..\InstLib;..\..\..\extras\xed-ia32\include\xed;..\..\..\extras\components\include;..\..\..\extras\stlport\include;..\..\..\extras;..\..\..\extras\libstdc++\include;..\..\..\extras\crt\include;..\..\..\extras\crt;..\..\..\extras\crt\include\arch-x86;..\..\..\extras\crt\include\kernel\uapi;..\..\..\extras\crt\include\kernel\uapi\asm-x86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>TARGET_IA32;HOST_IA32;TARGET_WINDOWS;WIN32;__PIN__=1;PIN_CRT=1;__i386__</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>
</ExceptionHandling>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<BufferSecurityCheck>false</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>
</DebugInformationFormat>
<DisableSpecificWarnings>4530;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<Link>
<AdditionalOptions>/export:main %(AdditionalOptions)</AdditionalOptions>
<AdditionalDependencies>pin.lib;xed.lib;pinvm.lib;kernel32.lib;pincrt.lib;ntdll-32.lib;crtbeginS.obj</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\..\ia32\lib;..\..\..\ia32\lib-ext;..\..\..\extras\xed-ia32\lib;..\..\..\ia32\runtime\pincrt;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>NotSet</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>
</EnableCOMDATFolding>
<LinkTimeCodeGeneration>
</LinkTimeCodeGeneration>
<EntryPointSymbol>Ptrace_DllMainCRTStartup%4012</EntryPointSymbol>
<BaseAddress>0x55000000</BaseAddress>
<TargetMachine>MachineX86</TargetMachine>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
</Midl>
<ClCompile>
<AdditionalOptions>/GR- /GS- /EHs- /EHa- /FP:strict /Oi- /FIinclude/msvc_compat.h %(AdditionalOptions)</AdditionalOptions>
<IntrinsicFunctions>false</IntrinsicFunctions>
<WholeProgramOptimization>false</WholeProgramOptimization>
<AdditionalIncludeDirectories>..\..\include\pin;..\..\include\pin\gen;..\InstLib;..\..\..\extras\xed-intel64\include\xed;..\..\..\extras\components\include;..\..\..\extras\stlport\include;..\..\..\extras;..\..\..\extras\libstdc++\include;..\..\..\extras\crt\include;..\..\..\extras\crt;..\..\..\extras\crt\include\arch-x86_64;..\..\..\extras\crt\include\kernel\uapi;..\..\..\extras\crt\include\kernel\uapi\asm-x86;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>TARGET_IA32E;HOST_IA32E;TARGET_WINDOWS;WIN32;__PIN__=1;PIN_CRT=1;__LP64__</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>
</ExceptionHandling>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<BufferSecurityCheck>false</BufferSecurityCheck>
<FunctionLevelLinking>true</FunctionLevelLinking>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>
</DebugInformationFormat>
<DisableSpecificWarnings>4530;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<Link>
<AdditionalOptions>/export:main %(AdditionalOptions)</AdditionalOptions>
<AdditionalDependencies>pin.lib;xed.lib;pinvm.lib;kernel32.lib;pincrt.lib;ntdll-64.lib;crtbeginS.obj</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\..\intel64\lib;..\..\..\intel64\lib-ext;..\..\..\extras\xed-intel64\lib;..\..\..\intel64\runtime\pincrt;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>NotSet</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>
</EnableCOMDATFolding>
<LinkTimeCodeGeneration>
</LinkTimeCodeGeneration>
<EntryPointSymbol>Ptrace_DllMainCRTStartup</EntryPointSymbol>
<BaseAddress>0xC5000000</BaseAddress>
<TargetMachine>MachineX64</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="library_trace.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="..\README" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
File diff suppressed because it is too large Load Diff
+25
View File
@@ -0,0 +1,25 @@
import logging
class CuteHandler(logging.StreamHandler):
def emit(self, record):
color = hash(record.name) % 7 + 31
try:
record.name = ("\x1b[%dm" % color) + record.name + "\x1b[0m"
except Exception:
pass
try:
record.msg = ("\x1b[%dm" % color) + record.msg + "\x1b[0m"
except Exception:
pass
super(CuteHandler, self).emit(record)
def getlogger(name):
logger = logging.getLogger(name)
logger.setLevel(logging.INFO)
stream_handler = CuteHandler()
stream_handler.setFormatter(logging.Formatter('%(levelname)-7s | %(asctime)-23s | %(name)-8s | %(message)s'))
logger.addHandler(stream_handler)
return logger
+3
View File
@@ -0,0 +1,3 @@
tqdm
networkx
matplotlib
+332
View File
@@ -0,0 +1,332 @@
#!/usr/bin/env python2
import os
import glob
import argparse
import signal
import typing
from util import exit_gracefully
from logger import *
from harconf import *
from template import *
from common import Synthesizer
"""
./syn-multi.py harness -t temptrace_math
./syn-multi.py harness -t temptrace_math --start test
./syn-multi.py harness -t temptrace_math --start test --sample-name input
"""
"""
[TARCE_DIR]
- cor1_1 : 1st trace using the 1st correct input
- cor1_2 : 2nd trace using the 1st correct input
- cor2_1 : 3rd trace using the 2nd correct input
- functype : function type information from IDA ==> fixed with dynamic information
- decompile: decompiled results where the library call started
[NOTE]
- cor1_1 and cor1_2 is used to infer the const value of argument
- cor1_1 and cor2_1 is used to infer the possible impact of file-related operation
"""
logger = getlogger("Synthesizer")
class Identifier:
def __init__(self, traces: typing.Dict[str, Synthesizer]):
self.cor_trace1 = traces["cor1"]
self.cor_trace2 = traces["cor2"]
self.diff_trace = traces["diff"]
# comparison result using same input should have result
self.comp_cor = self.compare_cortrace()
# comparison result using different inputs may not have result (i.e., null dict)
self.comp_diff = self.compare_difftrace()
self.report = self.make_report(self.comp_cor, self.comp_diff)
def make_report(self, cor_dict, diff_dict):
# NOTE: assume that diff_dict may not have any result
# - also, assume that the number of CIDs are same
out = {}
for cid in cor_dict.keys():
arg = cor_dict[cid]
if cid in diff_dict:
arg += "\n" + diff_dict[cid]
out[cid] = arg
return out
def analyze_args(self, args1, args2, cor):
# args e.g., [[(2291628, 'DP'), (11, 'D')], [(9376112, 'CP'), (2347535189, 'D')]]
# args e.g., [[(210, 'D')]]
# NOTE: we assume that the length of args are same
out = ""
if cor == True:
tag = " // [DIFF] (Multi-runs using same input)"
else:
tag = " // [DIFF] (Result with different inputs)"
for x in range(len(args1)):
rst = ""
# pointer
if args1[x][0][1] == 'DP' or args1[x][0][1] == 'CP':
if args1[x][1][0] == args2[x][1][0]:
rst = "SAME"
else:
rst = "DIFFERENT"
out += "\n // - Arg[%d]: %s (referenced value is %s) |" % (
x, NAMEDIC[args1[x][0][1]], rst)
# data
else:
if args1[x][0][0] == args2[x][0][0]:
rst = "SAME"
else:
rst = "DIFFERENT"
out += "\n // - Arg[%d]: DATA (value is %s) |" % (x, rst)
out = tag + out
return out
def compare_cortrace(self):
# compare cor_trace1 and cor_trace2
""" report
- arg1: data_pointer (referenced value SAME/DIFF), arg2: data (SAME)
- data_pointer / code_pointer / data, SAME or DIFF
"""
result = {}
for cid in self.cor_trace1.trace.cid_sequence:
if cid in self.cor_trace1.trace.calltrace and \
cid in self.cor_trace2.trace.calltrace:
calltrace1 = self.cor_trace1.trace.calltrace[cid]
calltrace2 = self.cor_trace2.trace.calltrace[cid]
rettrace1 = self.cor_trace1.trace.rettrace[cid]
rettrace2 = self.cor_trace2.trace.rettrace[cid]
funcname1 = calltrace1.dst_symbol
funcname2 = calltrace2.dst_symbol
analyzed_result = self.analyze_args(
calltrace1.args, calltrace2.args, cor=True)
result[cid] = analyzed_result
# print analyzed_result
else:
result[cid] = None
return result
def compare_difftrace(self):
# compare cor_Trace1 and diff_trace
# NOTE: we not only compare the argument if the length of cids are same
# - we don't provide alignment for this problem
result = {}
# check all function names for each sequence is same
for cid in self.cor_trace1.trace.cid_sequence:
if cid in self.cor_trace1.trace.calltrace and \
cid in self.diff_trace.trace.calltrace:
calltrace1 = self.cor_trace1.trace.calltrace[cid]
calltrace2 = self.diff_trace.trace.calltrace[cid]
funcname1 = calltrace1.dst_symbol
funcname2 = calltrace2.dst_symbol
if funcname1 != funcname2:
return result
for cid in self.cor_trace1.trace.cid_sequence:
if cid in self.cor_trace1.trace.calltrace and \
cid in self.diff_trace.trace.calltrace:
calltrace1 = self.cor_trace1.trace.calltrace[cid]
calltrace2 = self.diff_trace.trace.calltrace[cid]
rettrace1 = self.cor_trace1.trace.rettrace[cid]
rettrace2 = self.diff_trace.trace.rettrace[cid]
funcname1 = calltrace1.dst_symbol
funcname2 = calltrace2.dst_symbol
analyzed_result = self.analyze_args(
calltrace1.args, calltrace2.args, cor=False)
result[cid] = analyzed_result
# print analyzed_result
else:
result[cid] = None
return result
class MultiSynthesizer(Synthesizer):
# one of the core logics for discovering pointer relationship
def build_body(self, report):
# we should consider both call_tarce and ret_trace
# - report: dictionary from the diff analysis
pending_flag = True
pending_count = 0
prev_src_addr = 0
prev_dst_addr = 0
# 1) we need to handle used argument (raw value and pointer, ret-chain)
for cid in self.trace.cid_sequence:
variables = [] # defined variables: e.g., int a=0
arguments = [] # used arguments: func(&a)
calltrace = self.trace.calltrace[cid]
rettrace = self.trace.rettrace[cid]
funcname = calltrace.dst_symbol.decode()
fi = self.trace.find_function(calltrace.dst_addr)
ret_type = fi.ret_type
args_type = fi.args
msg = ""
args = calltrace.args
args_dump = calltrace.args_dump
args_ptr = calltrace.pointer
dst_addr = calltrace.dst_addr
src_addr = calltrace.src_addr
if src_addr == prev_src_addr and dst_addr == prev_dst_addr:
pending_count = pending_count + 1
pending_flag = True
else:
pending_count = 0
pending_flag = False
# one of core functions (we should do pointer analysis here)
need_to_define, arguments = self.ret_arg_code(
cid, args, args_dump, args_type, args_ptr)
input_digging_result = self.dig_userinput(
cid, args, args_dump, args_ptr)
need_to_define_str = ' '.join(need_to_define).strip()
self.history[cid] = (need_to_define, arguments)
# build function snippet
# print funcname, arguments
if funcname not in self.defined_func:
self.defined_func.append(funcname)
func_snippet = FUNC.replace("{funcname}", funcname)
else:
func_snippet = FUNC_WO.replace("{funcname}", funcname)
if need_to_define_str == '':
func_snippet = func_snippet.replace(
"{print_cid}", "// Harness function #%d " % cid)
else:
func_snippet = func_snippet.replace(
"{print_cid}", "// Harness function #%d \n %s" % (cid, ' '.join(need_to_define)))
func_snippet = func_snippet.replace(
"{arguments}", ', '.join(arguments))
if ret_type == '':
func_snippet = func_snippet.replace("{ret_statement}", "")
func_snippet = func_snippet.replace("{dbg_printf}",
'dbg_printf("%s\\n");' % (funcname))
else:
func_snippet = func_snippet.replace(
"{ret_statement}", "%s %s_ret_%d = " % (ret_type, funcname, cid))
func_snippet = func_snippet.replace("{dbg_printf}",
'dbg_printf("%s, ret = %%d\\n", %s_ret_%d);' % (funcname, funcname, cid))
msg += "\n%s" % report[cid]
# print "dist:", prev_src_addr, hex(abs (prev_src_addr - src_addr))
if prev_src_addr != 0 and abs(prev_src_addr - src_addr) > THRESHOLD:
msg += "\n // Distance between prev/current libcall:%s, we recomment you to check the condition between them " % (
hex(abs(prev_src_addr - src_addr)))
if pending_count > 0:
msg += "\n // [LOOP] This is %dth execution of %s() " % (
pending_count+1, funcname)
self.body.append(msg+func_snippet)
else:
self.body.append(msg+func_snippet)
prev_src_addr = src_addr
prev_dst_addr = dst_addr
def main():
# DEFINE PARSER
parser = argparse.ArgumentParser()
subparser = parser.add_subparsers(help="subparser")
# subparser: harness generation
har_parser = subparser.add_parser('harness')
har_parser.add_argument("-t", "--trace", dest="trace_dir", type=str,
default=None, help="Trace dir collected from DynamoRIO",
required=False)
har_parser.add_argument("-s", "--start", dest="start_func", type=str,
default=None, help="name of the starting function to process",
required=False)
har_parser.add_argument("-sample", "--sample-name", dest="sample_name", type=str,
default=None, help="name of the original sample name",
required=False)
har_parser.set_defaults(action='harness')
args = parser.parse_args()
# END PARSER
# Ctrl-c handler
signal.signal(signal.SIGINT, exit_gracefully(
signal.getsignal(signal.SIGINT)))
# Start harness synthesizer
if args.action == 'harness':
cor_trace_1 = glob.glob(os.path.join(
args.trace_dir, MAIN_TRACE) + "/*.log")[0]
cor_trace_2 = glob.glob(os.path.join(
args.trace_dir, SECOND_TRACE) + "/*.log")[0]
diff_trace = glob.glob(os.path.join(
args.trace_dir, DIFF_TRACE) + "/*.log")[0]
dumpdir = os.path.join(args.trace_dir, MAIN_TRACE, "memdump")
dumpdir2 = os.path.join(args.trace_dir, SECOND_TRACE, "memdump")
dumpdir_diff = os.path.join(args.trace_dir, DIFF_TRACE, "memdump")
functype_pn = os.path.join(args.trace_dir, "functype_")
syn_cor1 = MultiSynthesizer(cor_trace_1, dumpdir,
functype_pn, args.start_func, args.sample_name)
syn_cor2 = MultiSynthesizer(cor_trace_2, dumpdir2,
functype_pn, args.start_func, args.sample_name)
syn_diff = MultiSynthesizer(diff_trace, dumpdir_diff,
functype_pn, args.start_func, args.sample_name)
traces = {}
traces["cor1"] = syn_cor1
traces["cor2"] = syn_cor2
traces["diff"] = syn_diff
identifier = Identifier(traces)
report = identifier.report
syn_cor1.build_body(report)
syn_cor1.emit_code()
# syn.search_pointer(0x1f75ac0)
# syn.search_pointer(0x65aa89a0)
if __name__ == '__main__':
main()
+147
View File
@@ -0,0 +1,147 @@
#!/usr/bin/env python2
import argparse
import operator
import signal
from util import exit_gracefully
from logger import *
from harconf import *
from template import *
from common import SimpleTrace, Synthesizer
"""
./synthesizer.py harness -t temptrace/trace.log -d temptrace
./synthesizer.py harness -t temptrace/trace.log -d temptrace --start avformat_open_input
./synthesizer.py harness -t temptrace_gom/trace.log -d temptrace_gom -f temptrace_gom/functype --start avformat_open_input --sample-name small.mp4
./synthesizer.py diff --input-dummy temptrace_aimp/aimp_without_playlist.log --input-parse temptrace_aimp/aimp_with_playlist.log --output temptrace_aimp/trace.log
"""
logger = getlogger("Synthesizer")
class Differ:
def __init__(self, in_dummy_pn, in_parse_pn, output_pn):
self.in_dummy_pn = in_dummy_pn
self.in_parse_pn = in_parse_pn
self.output_pn = output_pn
self.dummy_trace = SimpleTrace(self.in_dummy_pn)
self.parse_trace = SimpleTrace(self.in_parse_pn)
dummy_trace_sort = sorted(
list(self.dummy_trace.unique_call.items()), key=operator.itemgetter(1))
parse_trace_sort = sorted(
list(self.parse_trace.unique_call.items()), key=operator.itemgetter(1))
dummy_calls = []
for k, v in dummy_trace_sort:
dummy_calls.append(k)
for k, v in parse_trace_sort:
if k not in dummy_calls:
print("CALLID[{0:05d}]: {1}".format(v, k))
# print len(self.dummy_trace.unique_call)
# print len(self.parse_trace.unique_call)
class SingleSynthesizer(Synthesizer):
def build_body(self):
# we should consider both call_tarce and ret_trace
# 1) we need to handle used argument (raw value and pointer, ret-chain)
for cid in self.trace.cid_sequence:
variables = [] # defined variables: e.g., int a=0
arguments = [] # used arguments: func(&a)
calltrace = self.trace.calltrace[cid]
rettrace = self.trace.rettrace[cid]
funcname = calltrace.dst_symbol.decode()
fi = self.trace.find_function(calltrace.dst_addr)
ret_type = fi.ret_type
args_type = fi.args
args = calltrace.args
args_dump = calltrace.args_dump
args_ptr = calltrace.pointer
# one of core functions (we should do pointer analysis here)
need_to_define, arguments = self.ret_arg_code(cid, args, args_dump, args_type, args_ptr)
need_to_define_str = ' '.join(need_to_define).strip()
self.history[cid] = (need_to_define, arguments)
func_snippet = FUNC.replace("{funcname}", funcname)
if need_to_define_str == '':
func_snippet = func_snippet.replace("{print_cid}", "/* Harness function #%d */" % cid)
else:
func_snippet = func_snippet.replace("{print_cid}", "/* Harness function #%d */\n %s" % (cid, ' '.join(need_to_define)))
func_snippet = func_snippet.replace("{arguments}", ', '.join(arguments))
if ret_type == '':
func_snippet = func_snippet.replace("{ret_statement}", "")
func_snippet = func_snippet.replace("{dbg_printf}",
'dbg_printf("%s\\n");' % (funcname))
else:
func_snippet = func_snippet.replace("{ret_statement}", "%s %s_ret = " % (ret_type, funcname))
func_snippet = func_snippet.replace("{dbg_printf}",
'dbg_printf("%s, ret = %%d\\n", %s_ret);' % (funcname, funcname))
self.body.append(func_snippet)
def main():
# DEFINE PARSER
parser = argparse.ArgumentParser()
subparser = parser.add_subparsers(help="subparser")
# subparser: harness generation
har_parser = subparser.add_parser('harness')
har_parser.add_argument("-t", "--trace", dest="trace_file", type=str,
default=None, help="Trace file collected from DynamoRIO",
required=False)
har_parser.add_argument("-d", "--memory-dump", dest="dump_dir", type=str,
default=None, help="memory dump file directory (pre/post)",
required=False)
har_parser.add_argument("-f", "--function-type", dest="functype", type=str,
default=None, help="function type information from IDA (and fixed pointer by trace)",
required=False)
har_parser.add_argument("-s", "--start", dest="start_func", type=str,
default=None, help="name of the starting function to process",
required=False)
har_parser.add_argument("-sample", "--sample-name", dest="sample_name", type=str,
default=None, help="name of the original sample name",
required=False)
har_parser.set_defaults(action='harness')
# subparser: diff trace
diff_parser = subparser.add_parser('diff')
diff_parser.add_argument('--input-dummy', dest="input_dummy", type=str)
diff_parser.add_argument('--input-parse', dest="input_parse", type=str)
diff_parser.add_argument('--output', dest="output", type=str)
diff_parser.set_defaults(action='diff')
args = parser.parse_args()
# END PARSER
# Ctrl-c handler
signal.signal(signal.SIGINT, exit_gracefully(signal.getsignal(signal.SIGINT)))
# Start harness synthesizer
if args.action == 'harness':
syn = SingleSynthesizer(args.trace_file, args.dump_dir,
args.functype, args.start_func, args.sample_name)
syn.build_body()
syn.emit_code()
# syn.search_pointer(0x1f75ac0)
# syn.search_pointer(0x65aa89a0)
elif args.action == 'diff':
diff = Differ(args.input_dummy, args.input_parse, args.output)
if __name__ == '__main__':
main()
+90
View File
@@ -0,0 +1,90 @@
ASCII_BYTE = ' !"#\\$%&\'\\(\\)\\*\\+,-\\./0123456789:;<=>\\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\\[\\]\\^_`abcdefghijklmnopqrstuvwxyz\\{\\|\\}\\\\~\t'
# Directory structure
MAIN_TRACE = 'cor1_1'
SECOND_TRACE = 'cor1_2'
DIFF_TRACE = 'cor2_1'
INPUT1 = 'input1'
INPUT2 = 'input2'
FUNCTYPE = 'functype'
HEADER = """
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <intrin.h>
#include <windows.h>
#include <tchar.h>
#include <strsafe.h>
#define dbg_printf (void)printf
// Macro to help to loading functions
#define LOAD_FUNC(h, n) \\
n##_func = (n##_func_t)GetProcAddress(h, #n); \\
if (!n##_func) { \\
dbg_printf("failed to load function " #n "\\n"); \\
exit(1); \\
}
// Macro help creating unique nop functions
#define NOP(x) \\
int nop##x() { \\
dbg_printf("==> nop%d called, %p\\n", ##x, _ReturnAddress());\\
return (DWORD)x; \\
}
HMODULE dlllib;
{typedef}
"""
FUZZME = """
void fuzz_me(char* filename){
{funcdef}
{harness}
}
"""
MAIN = """
int main(int argc, char ** argv)
{
if (argc < 2) {
printf("Usage %s: <input file>\\n", argv[0]);
printf(" e.g., harness.exe input\\n");
exit(1);
}
dlllib = LoadLibraryA("%s");
if (dlllib == NULL){
dbg_printf("failed to load library, gle = %d\\n", GetLastError());
exit(1);
}
char * filename = argv[1];
fuzz_me(filename);
return 0;
}
"""
"""
LOAD_FUNC(dlllib, avformat_open_input);
int ret_avformat_open_input = avformat_open_input_func(&ctx_org, filename, 0, &avformat_open_input_arg3); // zeros: if pointer one page ==> copy original page to that page ==> if error
dbg_printf("avformat_open_input: ret = %d\n", ret_avformat_open_input); // @jinho: check crash / progress
"""
FUNC = """
{print_cid}
LOAD_FUNC(dlllib, {funcname});
{ret_statement}{funcname}_func({arguments});
{dbg_printf} """
FUNC_WO = """
{print_cid}
{ret_statement}{funcname}_func({arguments});
{dbg_printf} """
+49
View File
@@ -0,0 +1,49 @@
import sys
import struct
import string
import signal
printable = set(string.printable.encode())
def strings(data: bytes):
found_str = b""
while True:
if not data:
break
for char in data:
if char in printable:
found_str += bytes([char])
elif len(found_str) >= 4:
yield found_str
break
break
yield b''
def u32(b, off=0):
return struct.unpack("<I", b[off:off+4])[0]
def p32(x):
return struct.pack("<I", x)
def exit_gracefully(original_sigint):
# code from: https://stackoverflow.com/questions/18114560/python-catch-ctrl-c-command-prompt-really-want-to-quit-y-n-resume-execution
def _exit_gracefully(signum, frame):
# restore the original signal handler as otherwise evil things will happen
# in raw_input when CTRL+C is pressed, and our signal handler is not re-entrant
signal.signal(signal.SIGINT, original_sigint)
try:
if input("\nReally quit? (y/n)> ").lower().startswith('y'):
sys.exit(1)
except KeyboardInterrupt:
print("Ok ok, quitting")
sys.exit(1)
# restore the exit gracefully handler here
signal.signal(signal.SIGINT, _exit_gracefully)
return _exit_gracefully
+54
View File
@@ -0,0 +1,54 @@
import json
import os
import typing
from idautils import Segments, Functions
from idc import get_segm_start, get_segm_end, get_func_name
import idaapi
def serialize(tif: idaapi.tinfo_t) -> typing.Union[dict, None]:
fi = idaapi.func_type_data_t()
if not tif.get_func_details(fi):
return None
args = [{"type": str(arg.type), "name": arg.name} for arg in fi]
return ({
'args': args,
'ret_type': str(fi.rettype),
'cc': '__stdcall' if fi.cc == idaapi.CM_CC_STDCALL else '__cdecl'
})
def main():
idaapi.auto_wait()
base = idaapi.get_imagebase()
tif = idaapi.tinfo_t()
f = open(os.environ.get("DESTPATH", "functype_"), 'w')
for ea in Segments():
# only code segment
if idaapi.segtype(ea) != idaapi.SEG_CODE:
continue
for fva in Functions(get_segm_start(ea), get_segm_end(ea)):
func_name = get_func_name(fva)
has_type = idaapi.get_tinfo(tif, fva) or idaapi.guess_tinfo(tif, fva)
if not has_type:
continue
info = serialize(tif)
if info is None:
continue
print(hex(fva-base)[:-1], "|", func_name, "|", tif, "|", len(info['args']))
f.write("0x%x|%s|%s\n" % (fva-base, func_name, json.dumps(info)))
f.close()
idaapi.qexit(0)
main()