strings.py
ofrak.core.strings
AsciiString (ResourceView)
dataclass
Resource representing a C-style, NULL-terminated string of ASCII characters. The text
string
is not NULL-terminated.
AsciiStringAnalyzer (Analyzer)
Extract the decoded string from a C-style, NULL-terminated string of ASCII characters.
analyze(self, resource, config)
async
Analyze a resource for to extract specific ResourceAttributes.
Users should not call this method directly; rather, they should run Resource.run or Resource.analyze.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
resource |
Resource |
The resource that is being analyzed |
required |
config |
None |
Optional config for analyzing. If an implementation provides a default, this default will always be used when config would otherwise be None. Note that a copy of the default config will be passed, so the default config values cannot be modified persistently by a component run. |
required |
Returns:
Type | Description |
---|---|
AsciiString |
The analysis results |
Source code in ofrak/core/strings.py
async def analyze(self, resource: Resource, config: None) -> AsciiString:
raw_without_null_byte = (await resource.get_data()).rstrip(b"\x00")
return AsciiString(raw_without_null_byte.decode("ascii"))
StringFindReplaceConfig (ComponentConfig)
dataclass
Attributes:
Name | Type | Description |
---|---|---|
to_find |
str |
the string to search for |
replace_with |
str |
the string to pass in |
null_terminate |
bool |
add a null terminator to the replacement if True |
allow_overflow |
bool |
allow the replace string to overflow the found string if True |
StringFindReplaceModifier (Modifier)
Find and replace all instances of a given string with a replacement string.
modify(self, resource, config)
async
Modify the given resource.
Users should not call this method directly; rather, they should run Resource.run.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
resource |
Resource |
required | |
config |
StringFindReplaceConfig |
Optional config for modification. If an implementation provides a default, this default will always be used when config would otherwise be None. Note that a copy of the default config will be passed, so the default config values cannot be modified persistently by a component run. |
required |
Source code in ofrak/core/strings.py
async def modify(self, resource: Resource, config: StringFindReplaceConfig) -> None:
to_find = config.to_find.encode("utf-8")
replace_with = config.replace_with.encode("utf-8") + (
b"\x00" if config.null_terminate and config.replace_with[-1] != "\x00" else b""
)
if not config.allow_overflow and len(replace_with) > len(to_find):
raise ModifierError(
f"Original string is longer than the new string ({len(to_find)} < "
f"{len(replace_with)})! Set config.allow_overflow = True to override this error. "
f"If you expect that the string to replace is null-terminated, then an overflow "
f"of one byte when config.null_terminate = True will not have any effect."
)
for offset in await resource.search_data(to_find):
await resource.run(BinaryPatchModifier, BinaryPatchConfig(offset, replace_with))
StringPatchingConfig (ComponentConfig)
dataclass
Dataclass required to apply a string patch with StringPatchingModifier
. The configuration
describes the offset
where the patch is to be applied, and the string
to patch in.
Attributes:
Name | Type | Description |
---|---|---|
offset |
int |
the offset at which to apply the patch |
string |
str |
the string to patch in |
StringPatchingModifier (Modifier)
Patch a string in a resource at a given offset, based on the provided configuration.
modify(self, resource, config)
async
Modify the given resource.
Users should not call this method directly; rather, they should run Resource.run.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
resource |
Resource |
required | |
config |
StringPatchingConfig |
Optional config for modification. If an implementation provides a default, this default will always be used when config would otherwise be None. Note that a copy of the default config will be passed, so the default config values cannot be modified persistently by a component run. |
required |
Source code in ofrak/core/strings.py
async def modify(self, resource: Resource, config: StringPatchingConfig):
new_data = config.string.encode("utf-8")
if config.null_terminate:
new_data += b"\x00"
patch_config = BinaryPatchConfig(config.offset, new_data)
await resource.run(BinaryPatchModifier, patch_config)
StringsUnpacker (Unpacker)
Unpack NULL-terminated strings of printable ASCII characters.
unpack(self, resource, config)
async
Unpack the given resource.
Users should not call this method directly; rather, they should run Resource.run or Resource.unpack.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
resource |
Resource |
The resource that is being unpacked |
required |
config |
None |
Optional config for unpacking. If an implementation provides a default, this default will always be used when config would otherwise be None. Note that a copy of the default config will be passed, so the default config values cannot be modified persistently by a component run. |
required |
Source code in ofrak/core/strings.py
async def unpack(self, resource: Resource, config: None) -> None:
if resource.get_data_id() is None:
return
if resource.has_tag(CodeRegion):
# code is less likely to have strings so more likely to have false positives
pattern = self.LONG_STRING_PATTERN
else:
pattern = self.SHORT_STRING_PATTERN
children = [
resource.create_child_from_view(
AsciiString(string.rstrip(b"\x00").decode("ascii")),
data_range=Range.from_size(offset, len(string)),
)
for offset, string in await resource.search_data(pattern)
]
await asyncio.gather(*children)