-
Notifications
You must be signed in to change notification settings - Fork 0
/
sys_scanner.py
172 lines (141 loc) · 5 KB
/
sys_scanner.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
import subprocess
import argparse
import pefile
import socket
import os
import sys
import tempfile
import shutil
import json
import re
# python sys_scanner.py config.json output.json
lib_func_with_syscalls = {}
def is_32bit_pe(file_path):
try:
pe = pefile.PE(file_path)
return pe.FILE_HEADER.Machine == pefile.MACHINE_TYPE["IMAGE_FILE_MACHINE_I386"]
except pefile.PEFormatError:
return False
def list_libraries(lib_path):
libraries = []
if os.path.isdir(lib_path):
for file in os.listdir(lib_path):
# check dll or exe
if file.endswith(".dll") or file.endswith(".exe"):
libraries.append(file)
return libraries
def collect_headers(headers_dir):
headers_str = ""
header_files = []
if not os.path.isdir(headers_dir):
raise Exception("Header path is not a directory")
for file in os.listdir(headers_dir):
# check dll or exe
if file.endswith(".h"):
print("Found header file: " + file)
header_files.append(file)
headers_str += open(os.path.join(headers_dir, file), "r").read() + "\n"
return header_files, headers_str
parser = argparse.ArgumentParser(description="IDA Pro Headless Syscall Scanner")
parser.add_argument("config", metavar="config", type=str, help="config file")
parser.add_argument("output", metavar="output", type=str, help="output json file")
args = parser.parse_args()
config = json.load(open(args.config, "r"))
ida_root_dir = config["ida_root_dir"]
script_path = os.path.dirname(os.path.realpath(__file__)) + "\\ida_s.py"
libs_dir = config["libs_dir"]
headers_dir = config["headers_dir"]
output_path = args.output
current_dir = os.getcwd()
# setup a listener to receive the script output from ida
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("localhost", 1338))
s.listen()
# list libraries to scan
libraries = list_libraries(libs_dir)
for lib in libraries:
# create a temporary directory for ida to work in
temp_dir = tempfile.mkdtemp()
# copy library to temporary directory
clibs_dir = os.path.join(libs_dir, lib)
shutil.copy(clibs_dir, temp_dir)
clibs_dir = os.path.join(temp_dir, lib)
# go to temporary directory
os.chdir(temp_dir)
if is_32bit_pe(clibs_dir):
ida_exe = f"{ida_root_dir}\\\\ida.exe"
else:
ida_exe = f"{ida_root_dir}\\\\ida64.exe"
print("Scanning {}".format(clibs_dir))
lib_func_with_syscalls[lib] = []
cmd = ['"{}"'.format(ida_exe), "-c", "-A", "-a", f'-S"{script_path}" {clibs_dir}']
cmd = " ".join(cmd)
print("Running: {}".format(cmd))
# run headless ida
subprocess.call(cmd)
# wait for ida script to finish
print("Waiting for IDA script output...")
conn, addr = s.accept()
print("[+] New client {}".format(addr))
while True:
# check connection status
data = conn.recv(65535)
if not data:
break
data = data.decode()
data = data.split("\n")
for line in data:
if "[+]" in line:
print(line)
continue
try:
func_name, func_addr = line.split(" ")
except ValueError:
print(line)
lib_func_with_syscalls[lib].append((func_name, int(func_addr, 16)))
conn.close()
# collect header files
headers_files, headers_str = collect_headers(headers_dir)
lib_func_with_syscalls_args = {}
for lib, functions in lib_func_with_syscalls.items():
lib_func_with_syscalls_args[lib] = {}
for func_name, func_addr in functions:
index = headers_str.find(func_name)
if index == -1:
lib_func_with_syscalls_args[lib][func_name] = {
"args": [],
"addr": func_addr,
}
continue
# make some replacements on arguments
func_def = headers_str[index:]
func_def = func_def[: func_def.find(";")]
func_def = (
func_def.replace("\t", "")
.replace(" ", " ")
.replace(", ", ",")
.replace(" ,", ",")
)
func_def = func_def[::-1].replace(")", "", 1)[::-1]
paren_index = func_def.find("(")
func_def = func_def[:paren_index] + "," + func_def[paren_index + 1 :]
func_def = [arg.strip() for arg in func_def.split(",\n")]
# remove empty arguments
if len(func_def) == 2 and func_def[1] == "":
lib_func_with_syscalls_args[lib][func_name] = {
"args": [],
"addr": func_addr,
}
# add the arguments
else:
lib_func_with_syscalls_args[lib][func_name] = {
"args": func_def[1:],
"addr": func_addr,
}
for lib, functions in lib_func_with_syscalls_args.items():
for func, args in functions.items():
print(f"{lib} {func} {args}")
# move back to the original directory
os.chdir(current_dir)
print(f"Saving output to {output_path}")
json.dump(lib_func_with_syscalls_args, open(output_path, "w"))