Skip to content

Commit fc18723

Browse files
committed
Format files to pass tests
1 parent ef8b16d commit fc18723

File tree

7 files changed

+116
-46
lines changed

7 files changed

+116
-46
lines changed

setup.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@
2424
"Operating System :: OS Independent",
2525
],
2626
entry_points={
27-
'console_scripts': [
28-
'karta_analyze_src = karta.karta_analyze_src:main'
29-
]
27+
'console_scripts': [
28+
'karta_analyze_src = karta.karta_analyze_src:main'
29+
]
3030
},
3131
zip_safe=False
32-
)
32+
)

src/karta/config/utils.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
DISASSEMBLER_PATH = None
1111
CONFIG_DIR = os.path.dirname(os.path.realpath(__file__))
1212
DEFAULT_DISASSEMBLER = os.path.join(CONFIG_DIR, "default_disassembler_path")
13-
SCRIPT_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)),"..","analyze_src_file.py")
13+
SCRIPT_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "analyze_src_file.py")
1414

1515
LIBRARY_NAME = "Karta"
1616
STATE_FILE_SUFFIX = "_file_state.json"
@@ -459,18 +459,42 @@ def isMatching():
459459
return matching_mode
460460

461461
def addDisassembler(name, path):
462+
"""Add an installation of a dissasembler.
463+
464+
Args:
465+
name (str): name of the installation file
466+
path (str): directory of the disassembler installtion
467+
"""
462468
with open(os.path.join(CONFIG_DIR, name), "w") as f:
463469
f.write(path)
464470

465471
def disassemblerInstallationExists(name):
472+
"""Check whether there is an existing installation with the filename.
473+
474+
Args:
475+
name (str): filename of the installtion to check
476+
477+
Return Value:
478+
return true if such installtion exists
479+
"""
466480
return os.path.exists(os.path.join(CONFIG_DIR, name))
467481

468482
def getDisassembler(name):
483+
"""Get directory of disassembler from configuration by it's configuration file name.
484+
485+
Args:
486+
name (str): name of the disassembler to search for
487+
"""
469488
if disassemblerInstallationExists(name):
470489
with open(os.path.join(CONFIG_DIR, name), "r") as f:
471490
return f.read()
472491

473492
def setDefaultDisassembler(name):
493+
"""Set the default disassembler in the configuration file.
494+
495+
Args:
496+
name (str): name of the file that contains the default disassembler to use.
497+
"""
474498
with open(os.path.join(CONFIG_DIR, DEFAULT_DISASSEMBLER), "w") as f:
475499
if os.path.isfile(os.path.join(CONFIG_DIR, name)):
476500
f.write(name)
@@ -488,7 +512,7 @@ def getDisasPath(prompter):
488512
actual_disas_path = None
489513
with open(DEFAULT_DISASSEMBLER, 'r') as f:
490514
actual_disas_path = f.read()
491-
full_disas_name_path = os.path.join(CONFIG_DIR ,actual_disas_path)
515+
full_disas_name_path = os.path.join(CONFIG_DIR, actual_disas_path)
492516
if os.path.isfile(full_disas_name_path):
493517
with open(full_disas_name_path, 'r') as f:
494518
DISASSEMBLER_PATH = f.read()

src/karta/disassembler/IDA/ida_cmd_api.py

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def name():
3333

3434
# Overridden base function
3535
async def createDatabase(self, binary_file, is_windows):
36-
"""Create a database file for the given binary file, compiled to windows or linux as specified.
36+
"""Create a database file asynchonously for the given binary file , compiled to windows or linux as specified.
3737
3838
Args:
3939
binary_file (path): path to the input binary (*.o / *.obj) file
@@ -49,36 +49,57 @@ async def createDatabase(self, binary_file, is_windows):
4949

5050
database_file = binary_file + self.suffix
5151
# execute the program
52-
process = await asyncio.create_subprocess_exec(self._path, "-A" , "-B", f"-T{type}" ,f"-o{database_file}", binary_file)
52+
process = await asyncio.create_subprocess_exec(self._path, "-A", "-B", f"-T{type}", f"-o{database_file}", binary_file)
5353
await process.wait()
5454
# return back the (should be) created database file path
5555
return database_file
5656

5757
# Overridden base function
5858
async def executeScript(self, database, script):
59-
"""Execute the given script over the given database file that was created earlier.
59+
"""Execute the given script asynchonously over the given database file that was created earlier.
6060
6161
Args:
6262
database (path): path to a database file created by the same program
6363
script (path): python script to be executed once the database is loaded
6464
"""
6565
process = await asyncio.create_subprocess_exec(self._path, "-A", f"-S{script}", database)
6666
await process.wait()
67-
67+
6868
def isSupported(self, feature_name):
69+
"""Check if feature is enabled.
70+
71+
Args:
72+
feature_name (str): name of the feature to check if enabled
73+
74+
Return Value:
75+
returns whether the current class has a member name "feature_name"
76+
"""
6977
return hasattr(self, feature_name)
7078

7179
async def createAndExecute(self, binary_file, is_windows, script):
80+
"""Execute the given script over the given database asynchonously without closing it first.
81+
82+
Args:
83+
binary_file (str): filename of the binary to analyze
84+
is_windows (bool): whether the file is a windows compiled file or linux compiled file
85+
script (str): filename of the script to use on the binary in ida
86+
"""
7287
type = "elf" if not is_windows else "coff"
7388

7489
if not hasattr(self, "is64"):
7590
self.decideArchitecureChoices(binary_file, is_windows)
7691

7792
# execute the program
78-
process = await asyncio.create_subprocess_exec(self._path, "-A", "-c" , f"-S{script}", f"-T{type}", binary_file)
93+
process = await asyncio.create_subprocess_exec(self._path, "-A", "-c", f"-S{script}", f"-T{type}", binary_file)
7994
await process.wait()
8095

8196
def decideArchitecureChoices(self, binary_file, is_windows):
97+
"""Automate deciding whether to send the files to ida64 or ida.
98+
99+
Args:
100+
binary_file (str): filename of the file to be a test case for analysis
101+
is_windows (bool): whether the test file is a linux or windows binary
102+
"""
82103
# machine type header of pe
83104
# specified in that order, amd64, arm64, ia64, loongarch64, riscv64
84105
ARCH64PE = [b"\x64\x86", b"\x64\xaa", b"\x00\x02", b"\x64\x62", b"\x64\x50"]
@@ -106,6 +127,7 @@ def decideArchitecureChoices(self, binary_file, is_windows):
106127
self.is64 = True
107128
self.suffix = ".i64" if self.is64 else ".idb"
108129
self._path += "64" if self.is64 else ""
109-
130+
131+
110132
# Don't forget to register at the factory
111133
registerDisassemblerCMD(IdaCMD.identify, IdaCMD)

src/karta/disassembler/disas_api.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
##################################################################################################################
44

55
import os
6-
import asyncio
76
from collections import defaultdict
87

98
class DisasAPI(object):
@@ -553,7 +552,7 @@ def name():
553552
raise NotImplementedError("Subclasses should implement this!")
554553

555554
async def createDatabase(self, binary_file, is_windows):
556-
"""Create a database file for the given binary file, compiled to windows or linux as specified.
555+
"""Create a database file asynchonously for the given binary file, compiled to windows or linux as specified.
557556
558557
Args:
559558
binary_file (path): path to the input binary (*.o / *.obj) file
@@ -565,15 +564,20 @@ async def createDatabase(self, binary_file, is_windows):
565564
raise NotImplementedError("Subclasses should implement this!")
566565

567566
async def executeScript(self, database, script):
568-
"""Execute the given script over the given database file that was created earlier.
567+
"""Execute the given script asynchonously over the given database file that was created earlier.
569568
570569
Args:
571570
database (path): path to a database file created by the same program
572571
script (path): python script to be executed once the database is loaded
573572
"""
574573
raise NotImplementedError("Subclasses should implement this!")
575-
574+
576575
def isSupported(self, feature_name):
576+
"""Allow script to query whether a feature is enabled in it's disas_api.
577+
578+
Return Value:
579+
False, no featue is supported on this Abstract class
580+
"""
577581
return False
578582

579583
class DisasVerifier(object):

src/karta/installers/common_installer.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@
44
from ..config.utils import addDisassembler, getDisassembler, setDefaultDisassembler
55

66

7-
def common_locations():
8-
"""
9-
Gets a list of common places where a disassembler may be installed
10-
"""
7+
def commonLocations():
8+
"""Get a list of common places where a disassembler may be installed on any os."""
119
location_list = list()
1210
# based on the operating system installation location may vary
1311
system_lower = platform.system().lower()
@@ -30,18 +28,21 @@ def common_locations():
3028
return location_list
3129

3230

33-
def detect_installation(pattern, installation_file):
34-
"""
35-
If already installed get the installation path
36-
Detect installation of a disassembler by a regex provided by its specific installer
37-
Save it into a file so you may use it as a default
38-
If its not in any of the common locations ask the user to enter it
31+
def detectInstallation(pattern, installation_file):
32+
"""Detect a disassembler's installation by regex, if not found get it with input.
33+
34+
Args:
35+
pattern (re.Pattern): pattern to detect an installation
36+
installation_file (str): name of the file to save the disassembler name in
37+
38+
Return Value:
39+
Directory of the disassembler file
3940
"""
4041
disassembler = getDisassembler(installation_file)
4142
if disassembler is not None:
4243
return disassembler
4344

44-
for location in common_locations():
45+
for location in commonLocations():
4546
for directory in next(os.walk(location))[1]:
4647
if pattern.match(directory):
4748
install_directory = os.path.join(location, directory)
@@ -59,5 +60,6 @@ def detect_installation(pattern, installation_file):
5960
exit(0)
6061

6162

62-
def set_default_disassembler(disassembler_name):
63+
def commonSetDefaultDisassembler(disassembler_name):
64+
"""Call config utils setDefaultDisassembler function."""
6365
setDefaultDisassembler(disassembler_name)

src/karta/installers/ida_installer.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,27 @@
22
import re
33
from shutil import copyfile
44

5-
from .common_installer import detect_installation, set_default_disassembler
5+
from .common_installer import detectInstallation, commonSetDefaultDisassembler
66

77

88
disassembler_name = "ida_path"
99

10-
def detect_ida(save_file):
11-
"""
12-
find where ida is installed by this regex, should work for both windows and linux
10+
def detectIda(save_file):
11+
"""Find where ida is installed by this regex, should work for both windows and linux.
12+
13+
Args:
14+
save_file (str): name of the file which will save the path to the disassembler directory
15+
16+
Return Value:
17+
Installation folder for the ida pro disassembler
1318
"""
14-
pattern = re.compile("ida( pro )?-?\d\.\d", re.IGNORECASE)
15-
return detect_installation(pattern, save_file)
19+
pattern = re.compile("ida( pro )?-?\\d\\.\\d", re.IGNORECASE)
20+
return detectInstallation(pattern, save_file)
1621

1722
def main():
18-
"""
19-
detect ida copy library files into its plugins directory and add the plugin to be run on start
20-
"""
21-
path = detect_ida(disassembler_name)
22-
set_default_disassembler(disassembler_name)
23+
"""Detect ida copy plugin file into its plugins directory."""
24+
path = detectIda(disassembler_name)
25+
commonSetDefaultDisassembler(disassembler_name)
2326
src_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..')
2427
ida_plugin = os.path.join(src_dir, "plugins", "ida_plugin.py")
2528
plugin_dst = os.path.join(path, "plugins", "ida_karta.py")
@@ -29,6 +32,5 @@ def main():
2932
)
3033

3134

32-
3335
if __name__ == "__main__":
3436
main()

src/karta/karta_analyze_src.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def locateFiles(bin_dir, file_list, suffix):
4848
yield path.abspath(path.join(root, file)), file
4949

5050
async def analyzeFile(full_file_path, is_windows):
51-
"""Analyze a single file using analyzer script.
51+
"""Analyze a single file asynchonously using analyzer script.
5252
5353
Args:
5454
full_file_path (str): full path to the specific (*.obj / *.o) file
@@ -61,6 +61,16 @@ async def analyzeFile(full_file_path, is_windows):
6161
await disas_cmd.executeScript(database_path, SCRIPT_PATH)
6262

6363
async def processFile(full_file_path, is_windows, compiled_file, progress_bar, prompter, semaphore):
64+
"""Analyze a file asynchonously using a disassembler and parse the file stats.
65+
66+
Args:
67+
full_file_path (str): full path to the specific (*.obj / *.o) file
68+
is_windows (bool): True iff a windows compilation (*.obj or *.o)
69+
compiled_file (str): file name of the compiled_file
70+
progress_bar (progressBar): progress bar to update once file processing is finished
71+
prompter (prompter): notify the user when an error occured
72+
semaphore (semaphore): release it when the disassembler finished processing our file
73+
"""
6474
# run the analsys on the files in an asynchrnous way
6575
prompter.debug(f"{full_file_path} - {compiled_file}")
6676
if progress_bar is None:
@@ -261,7 +271,14 @@ async def analyzeLibrary(config_name, bin_dirs, compiled_ars, concurrency, promp
261271
prompter.info(f"Anchor to function ratio is: {len(anchors_list)}/{len(src_functions_list)}")
262272
prompter.removeIndent()
263273

264-
def verify_archives_and_objects(using_archives, couples, prompter):
274+
def verifyArchivesAndObjects(using_archives, couples, prompter):
275+
"""Verify that the archive and object files karta processes are valid.
276+
277+
Args:
278+
using_archives (bool): is karta analyzing object files only or library files too
279+
couples (list): couples of dir and lib files or only bin dirs according to the using_archives parameter
280+
prompter (prompter): prompter to notify the user if any error occures
281+
"""
265282
bin_dirs = []
266283
archive_paths = []
267284
error_occured = False
@@ -275,7 +292,7 @@ def verify_archives_and_objects(using_archives, couples, prompter):
275292
error_occured = True
276293
else:
277294
bin_dirs.append(couples[i])
278-
295+
279296
if not path.exists(couples[i + 1]):
280297
prompter.error(f"Error the path {couples[i + 1]} does not exist!")
281298
error_occured = True
@@ -332,8 +349,8 @@ def main(args=None):
332349
if using_archives:
333350
if len(couples) % 2 != 0:
334351
parser.error("Odd length in list of dir,archive couples, should be: [(directory, archive name), ...]")
335-
336-
paths = verify_archives_and_objects(using_archives, couples, prompter)
352+
353+
paths = verifyArchivesAndObjects(using_archives, couples, prompter)
337354

338355
if paths is None:
339356
return
@@ -354,7 +371,6 @@ def main(args=None):
354371
if is_windows:
355372
setWindowsMode()
356373

357-
358374
# analyze the open source library
359375
asyncio.run(analyzeLibrary(constructConfigPath(library_name, library_version), bin_dirs, archive_paths, concurrency, prompter))
360376

0 commit comments

Comments
 (0)