-
Notifications
You must be signed in to change notification settings - Fork 5
Ld preload symbol #510
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Ld preload symbol #510
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -352,7 +352,7 @@ def __init__(self, arch_info, inits): | |
| self.patch_name = "base" | ||
| self.enabled = True | ||
|
|
||
| self.set_arch_info(arch_info) | ||
| self.set_arch_info(arch_info[0]) | ||
|
|
||
| if len(inits): | ||
| self.igloo_init = inits[0] | ||
|
|
@@ -1642,3 +1642,38 @@ def generate(self, patches): | |
|
|
||
| if len(results): | ||
| return {'pseudofiles': results} | ||
|
|
||
|
|
||
| class LinkerSymbolSearch(PatchGenerator): | ||
| ''' | ||
| During static analysis the LibrarySymbols class collected | ||
| key->value mappings from libraries exporting some common nvram | ||
| defaults symbols ("Nvrams", "router_defaults") - add these to our | ||
| nvram config if we have any. | ||
| ''' | ||
|
|
||
| def __init__(self, library_info, archid): | ||
| self.library_info = library_info | ||
| self.patch_name = "ld.01_library" | ||
| self.linker_paths = archid[1] | ||
| self.enabled = True | ||
|
|
||
| def generate(self, patches): | ||
| sources = self.library_info.get("symbols", {}) | ||
| if not len(sources): | ||
| return | ||
| linkers_without_preload = [] | ||
| linkers = self.linker_paths.copy() | ||
|
|
||
| for file in sources.keys(): | ||
| if file in self.linker_paths.keys(): | ||
| self.linker_paths.pop(file) | ||
| if not ("_dl_preload" in sources[file] or "handle_ld_preload" in sources[file]): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if this list is exhaustive... we'll do some experiments to see what we have in the corpus? |
||
| linkers_without_preload.append(file) | ||
| for x in sources[file]: | ||
| if "GLIBC_" in x: | ||
| linkers_without_preload.remove(file) | ||
| break | ||
| if not len(linkers_without_preload): | ||
| return | ||
| logger.critical(f"The following linkers are missing PRELOAD capabilites: {linkers_without_preload} out of {linkers.keys()}") | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,7 +37,7 @@ def find_regex(target_regex, extract_root, ignore=None, only_files=True): | |
| if filepath.startswith(os.path.join(extract_root, "igloo")): | ||
| continue | ||
|
|
||
| # skip non-regular files if `only_files` is true | ||
| # skip non-regular.. files if `only_files` is true | ||
| if only_files and not os.path.isfile(filepath): | ||
| continue | ||
|
|
||
|
|
@@ -81,6 +81,7 @@ def run(self, extracted_fs, prior_results): | |
| ''' | ||
|
|
||
| arch_counts = {32: Counter(), 64: Counter(), "unknown": 0} | ||
| loaders = {} | ||
| for root, _, files in os.walk(extracted_fs): | ||
| for file_name in files: | ||
| path = os.path.join(root, file_name) | ||
|
|
@@ -101,6 +102,20 @@ def run(self, extracted_fs, prior_results): | |
| logger.warning(f"Failed to parse ELF file {path}: {e}. Ignoring") | ||
| continue | ||
| info = arch_filter(ef) | ||
| for segment in ef.iter_segments(type='PT_INTERP'): | ||
| try: | ||
| name = segment.get_interp_name() | ||
| if name is not None: | ||
| # handle non path loaders might be a problem on certain version where loader path is handled with an env var | ||
| if '/' not in name: | ||
| for loader in loaders.keys(): | ||
| if name in loader: | ||
| loaders[loader] = loaders.get(loader, 0) + 1 | ||
| break | ||
| else: | ||
| loaders[name] = loaders.get(name, 0) + 1 | ||
| except AttributeError: | ||
| continue | ||
| if info.bits is None or info.arch is None: | ||
| arch_counts["unknown"] += 1 | ||
| else: | ||
|
|
@@ -145,7 +160,7 @@ def run(self, extracted_fs, prior_results): | |
| raise ValueError("Failed to determine architecture of filesystem") | ||
|
|
||
| logger.debug(f"Identified architecture: {best}") | ||
| return best | ||
| return (best, loaders) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we consider a dict here?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It depends on whether we will extract more info as time goes on. As it is I special cased the dumping process to avoid dumping loaders |
||
|
|
||
| @staticmethod | ||
| def _binary_filter(fsbase, name): | ||
|
|
@@ -653,7 +668,8 @@ class LibrarySymbols(StaticAnalysis): | |
|
|
||
| def run(self, extract_dir, prior_results): | ||
| self.extract_dir = extract_dir | ||
| self.archend = arch_end(prior_results['ArchId']) | ||
| self.archend = arch_end(prior_results['ArchId'][0]) | ||
| self.linkers = prior_results['ArchId'][1] | ||
|
|
||
| if any([x is None for x in self.archend]): | ||
| self.enabled = False | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we update the comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah I thought I had