Skip to content

Commit 8dc0e67

Browse files
committed
Fix type checks and improve config parsing logic
Replaced type() checks with isinstance() for better subclass handling in rat_config_parser.py. Fixed variable naming and logic errors in config_decryptor_random_hardcoded.py. Improved key extraction logic in config_decryptor_rijndael.py. Refactored bytes/int conversion utility functions for clarity and correctness. Optimized CustomAttribute lookup in dotnetpe_payload.py for efficiency.
1 parent 95877b8 commit 8dc0e67

5 files changed

Lines changed: 37 additions & 25 deletions

File tree

src/rat_king_parser/config_parser/rat_config_parser.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,13 @@ def __init__(
9292
# Assigned in _decrypt_and_decode_config()
9393
self._decryptor: ConfigDecryptor = None
9494
self.report["config"] = self._get_config()
95-
self.report["key"] = (
96-
self._decryptor.key.hex()
97-
if self._decryptor is not None and self._decryptor.key is not None
98-
else "None"
99-
)
95+
key_hex = "None"
96+
if self._decryptor is not None and self._decryptor.key is not None:
97+
if isinstance(self._decryptor.key, bytes):
98+
key_hex = self._decryptor.key.hex()
99+
else:
100+
key_hex = str(self._decryptor.key)
101+
self.report["key"] = key_hex
100102
self.report["salt"] = (
101103
self._decryptor.salt.hex()
102104
if self._decryptor is not None and self._decryptor.salt is not None
@@ -122,7 +124,7 @@ def _decrypt_and_decode_config(
122124
config_fields_map[k] = field_name
123125
item_data[field_name] = v
124126
if len(item_data) > 0:
125-
if type(item) is config_item.EncryptedStringConfigItem:
127+
if isinstance(item, config_item.EncryptedStringConfigItem):
126128
# Translate config value RVAs to string values
127129
for k in item_data:
128130
item_data[k] = self._dnpp.user_string_from_rva(item_data[k])
@@ -159,7 +161,7 @@ def _decrypt_and_decode_config(
159161
if self._decryptor is None:
160162
raise ConfigParserException("All decryptors failed")
161163

162-
elif type(item) is config_item.ByteArrayConfigItem:
164+
elif isinstance(item, config_item.ByteArrayConfigItem):
163165
for k in item_data:
164166
arr_size, arr_rva = item_data[k]
165167
item_data[k] = self._dnpp.byte_array_from_size_and_rva(
@@ -168,7 +170,7 @@ def _decrypt_and_decode_config(
168170

169171
decoded_config.update(item_data)
170172
# UrlHost is a marker of a special case until this can be standardized
171-
if len(decoded_config) < min_config_len and "UrlHost" not in item_data:
173+
if len(decoded_config) < min_config_len and "UrlHost" not in decoded_config:
172174
raise ConfigParserException(
173175
f"Minimum threshold of config items not met: {len(decoded_config)}/{min_config_len}"
174176
)

src/rat_king_parser/config_parser/utils/data_utils.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@
3131

3232

3333
# Converts a bytes object to an int object using the specified byte order
34-
def bytes_to_int(bytes: bytes, order: str = "little") -> int:
34+
def bytes_to_int(data: bytes, order: str = "little") -> int:
3535
try:
36-
return int.from_bytes(bytes, byteorder=order)
36+
return int.from_bytes(data, byteorder=order)
3737
except Exception:
38-
raise ConfigParserException(f"Error parsing int from value: {bytes}")
38+
raise ConfigParserException(f"Error parsing int from value: {data}")
3939

4040

4141
# Decodes a bytes object to a Unicode string, using UTF-16LE for byte values
@@ -57,8 +57,8 @@ def decode_bytes(byte_str: bytes | str) -> str:
5757

5858

5959
# Converts an int to a bytes object, with the specified length and order
60-
def int_to_bytes(int: int, length: int = 4, order: str = "little") -> bytes:
60+
def int_to_bytes(value: int, length: int = 4, order: str = "little") -> bytes:
6161
try:
62-
return int.to_bytes(length, order)
62+
return value.to_bytes(length, order)
6363
except Exception:
64-
raise ConfigParserException(f"Error parsing bytes from value: {int}")
64+
raise ConfigParserException(f"Error parsing bytes from value: {value}")

src/rat_king_parser/config_parser/utils/decryptors/config_decryptor_random_hardcoded.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,9 @@ def _get_hardcoded_hosts(self) -> list[str]:
7979
hardcoded_hosts = []
8080
for rva in hardcoded_host_rvas:
8181
try:
82-
harcoded_host = self._payload.user_string_from_rva(bytes_to_int(rva))
83-
if harcoded_host != ".":
84-
hardcoded_hosts.append(harcoded_host)
82+
hardcoded_hosts = self._payload.user_string_from_rva(bytes_to_int(rva))
83+
if hardcoded_hosts != ".":
84+
hardcoded_hosts.append(hardcoded_hosts)
8585
except Exception as e:
8686
logger.error(f"Error translating hardcoded host at {hex(rva)}: {e}")
8787
continue

src/rat_king_parser/config_parser/utils/decryptors/config_decryptor_rijndael.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,19 +102,22 @@ def decrypt_encrypted_strings(self, encrypted_strings: dict[str, str]) -> dict[s
102102
logger.debug("Decrypting encrypted strings...")
103103
if not self.key:
104104
try:
105-
#
106105
raw_key_field = self._payload.field_name_from_rva(self._key_rva)
107106
if raw_key_field in encrypted_strings:
108107
key = encrypted_strings[raw_key_field]
109108
self.key = self._derive_key(key)
110109
else:
111110
for key_pattern in self._KEY_PATTERNS:
112111
key_hit = search(key_pattern, self._payload.data)
112+
if not key_hit:
113+
continue
113114
key_rva = bytes_to_int(key_hit.groups()[0])
114115
raw_key_field = self._payload.field_name_from_rva(key_rva)
115-
key = encrypted_strings[raw_key_field]
116-
self.key = self._derive_key(key)
117-
break
116+
if raw_key_field in encrypted_strings:
117+
key = encrypted_strings[raw_key_field]
118+
self.key = self._derive_key(key)
119+
break
120+
118121
except Exception as e:
119122
raise ConfigParserException(f"Failed to derive AES key: {e}")
120123

src/rat_king_parser/config_parser/utils/dotnetpe_payload.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,13 @@ def custom_attribute_from_type(self, typespacename: str, typename: str) -> dict:
234234
"""
235235
config = {}
236236
try:
237+
ca_map = {}
238+
for ca in self.dotnetpe.net.mdtables.CustomAttribute.rows:
239+
idx = ca.Parent.row_index
240+
if idx not in ca_map:
241+
ca_map[idx] = []
242+
ca_map[idx].append(ca)
243+
237244
for td in self.dotnetpe.net.mdtables.TypeDef.rows:
238245
if (
239246
td.TypeNamespace.value != typespacename
@@ -253,10 +260,10 @@ def custom_attribute_from_type(self, typespacename: str, typename: str) -> dict:
253260
"String_",
254261
):
255262
continue
256-
for ca in self.dotnetpe.net.mdtables.CustomAttribute.rows:
257-
if (
258-
ca.Parent.row_index == pd_row_index + 1
259-
): # CustomAttribute Parent index is 1-based
263+
# CustomAttribute Parent index is 1-based
264+
target_index = pd_row_index + 1
265+
if target_index in ca_map:
266+
for ca in ca_map[target_index]:
260267
# Extract the value from the CustomAttribute blob
261268
blob_data = ca.Value.value
262269
if blob_data and blob_data != b"\x01\x00\x00\x00":

0 commit comments

Comments
 (0)