3434from re import DOTALL , compile , search
3535from typing import Any , Tuple
3636
37- # from yara import Rules
38-
3937from .config_parser_exception import ConfigParserException
4038from .utils import config_item
41- from .utils .decryptors import (
42- SUPPORTED_DECRYPTORS ,
43- ConfigDecryptor ,
44- IncompatibleDecryptorException ,
45- )
39+ from .utils .decryptors import SUPPORTED_DECRYPTORS , ConfigDecryptor , IncompatibleDecryptorException
4640from .utils .dotnetpe_payload import DotNetPEPayload
4741
42+ # from yara import Rules
43+
44+
4845logger = getLogger (__name__ )
4946
5047
@@ -54,9 +51,7 @@ class RATConfigParser:
5451 _MIN_CONFIG_LEN_CEILING = 9
5552
5653 # Pattern to find the VerifyHash() method
57- _PATTERN_VERIFY_HASH = compile (
58- rb"\x7e.{3}\x04(?:\x6f.{3}\x0a){2}\x74.{3}\x01" , DOTALL
59- )
54+ _PATTERN_VERIFY_HASH = compile (rb"\x7e.{3}\x04(?:\x6f.{3}\x0a){2}\x74.{3}\x01" , DOTALL )
6055
6156 # def __init__(self, file_path: str, yara_rule: Rules = None) -> None:
6257 def __init__ (self , file_data : bytes = None ) -> None :
@@ -77,33 +72,24 @@ def __init__(self, file_data: bytes = None) -> None:
7772 self ._decryptor : ConfigDecryptor = None
7873 self .report ["config" ] = self ._get_config ()
7974 self .report ["key" ] = (
80- self ._decryptor .key .hex ()
81- if self ._decryptor is not None and self ._decryptor .key is not None
82- else "None"
75+ self ._decryptor .key .hex () if self ._decryptor is not None and self ._decryptor .key is not None else "None"
8376 )
8477 self .report ["salt" ] = (
85- self ._decryptor .salt .hex ()
86- if self ._decryptor is not None and self ._decryptor .salt is not None
87- else "None"
78+ self ._decryptor .salt .hex () if self ._decryptor is not None and self ._decryptor .salt is not None else "None"
8879 )
8980 except Exception as e :
9081 # self.report["config"] = f"Exception encountered for {file_path}: {e}"
9182 self .report ["config" ] = f"Exception encountered: { e } "
9283
9384 # Decrypts/decodes values from an encrypted config and returns the
9485 # decrypted/decoded config
95- def _decrypt_and_decode_config (
96- self , encrypted_config : bytes , min_config_len : int
97- ) -> dict [str , Any ]:
86+ def _decrypt_and_decode_config (self , encrypted_config : bytes , min_config_len : int ) -> dict [str , Any ]:
9887 decoded_config = {}
9988
10089 for item_class in config_item .SUPPORTED_CONFIG_ITEMS :
10190 item = item_class ()
10291 # Translate config Field RVAs to Field names
103- item_data = {
104- self ._dnpp .field_name_from_rva (k ): v
105- for k , v in item .parse_from (encrypted_config ).items ()
106- }
92+ item_data = {self ._dnpp .field_name_from_rva (k ): v for k , v in item .parse_from (encrypted_config ).items ()}
10793
10894 if len (item_data ) > 0 :
10995 if type (item ) is config_item .EncryptedStringConfigItem :
@@ -122,22 +108,16 @@ def _decrypt_and_decode_config(
122108 try :
123109 self ._decryptor = decryptor (self ._dnpp )
124110 except IncompatibleDecryptorException as ide :
125- logger .debug (
126- f"Decryptor incompatible { decryptor } : { ide } "
127- )
111+ logger .debug (f"Decryptor incompatible { decryptor } : { ide } " )
128112 self ._incompatible_decryptors .append (decryptor )
129113 continue
130114 try :
131115 # Try to decrypt the encrypted strings
132116 # Continue to next compatible decryptor on failure
133- item_data = self ._decryptor .decrypt_encrypted_strings (
134- item_data
135- )
117+ item_data = self ._decryptor .decrypt_encrypted_strings (item_data )
136118 break
137119 except Exception as e :
138- logger .debug (
139- f"Decryption failed with decryptor { decryptor } : { e } "
140- )
120+ logger .debug (f"Decryption failed with decryptor { decryptor } : { e } " )
141121 self ._decryptor = None
142122
143123 if self ._decryptor is None :
@@ -146,16 +126,12 @@ def _decrypt_and_decode_config(
146126 elif type (item ) is config_item .ByteArrayConfigItem :
147127 for k in item_data :
148128 arr_size , arr_rva = item_data [k ]
149- item_data [k ] = self ._dnpp .byte_array_from_size_and_rva (
150- arr_size , arr_rva
151- ).hex ()
129+ item_data [k ] = self ._dnpp .byte_array_from_size_and_rva (arr_size , arr_rva ).hex ()
152130
153131 decoded_config .update (item_data )
154132
155133 if len (decoded_config ) < min_config_len :
156- raise ConfigParserException (
157- f"Minimum threshold of config items not met: { len (decoded_config )} /{ min_config_len } "
158- )
134+ raise ConfigParserException (f"Minimum threshold of config items not met: { len (decoded_config )} /{ min_config_len } " )
159135 return decoded_config
160136
161137 # Searches for the RAT configuration section, using the VerifyHash() marker
@@ -185,38 +161,29 @@ def _get_config_cctor_brute_force(self) -> Tuple[int, dict[str, Any]]:
185161 raise ConfigParserException ("No .cctor method could be found" )
186162
187163 # For each .cctor method, map its RVA and body (in raw bytes)
188- candidate_cctor_data = {
189- method .rva : self ._dnpp .method_body_from_method (method )
190- for method in candidates
191- }
164+ candidate_cctor_data = {method .rva : self ._dnpp .method_body_from_method (method ) for method in candidates }
192165
193166 config_start , decrypted_config = None , None
194167 # Start at our ceiling value for number of config items
195168 min_config_len = self ._MIN_CONFIG_LEN_CEILING
196169
197170 while decrypted_config is None and min_config_len >= self ._MIN_CONFIG_LEN_FLOOR :
198171 for method_rva , method_body in candidate_cctor_data .items ():
199- logger .debug (
200- f"Attempting brute force at .cctor method at { hex (method_rva )} "
201- )
172+ logger .debug (f"Attempting brute force at .cctor method at { hex (method_rva )} " )
202173 try :
203174 config_start , decrypted_config = (
204175 method_rva ,
205176 self ._decrypt_and_decode_config (method_body , min_config_len ),
206177 )
207178 break
208179 except Exception as e :
209- logger .debug (
210- f"Brute force failed for method at { hex (method_rva )} : { e } "
211- )
180+ logger .debug (f"Brute force failed for method at { hex (method_rva )} : { e } " )
212181 continue
213182 # Reduce the minimum config length until we reach our floor
214183 min_config_len -= 1
215184
216185 if decrypted_config is None :
217- raise ConfigParserException (
218- "No valid configuration could be parsed from any .cctor methods"
219- )
186+ raise ConfigParserException ("No valid configuration could be parsed from any .cctor methods" )
220187 return config_start , decrypted_config
221188
222189 # Attempts to retrieve the config via looking for a config section preceded
@@ -230,16 +197,12 @@ def _get_config_verify_hash_method(self) -> Tuple[int, dict[str, Any]]:
230197
231198 # Reverse the hit to find the VerifyHash() method, then grab the
232199 # subsequent function
233- config_method = self ._dnpp .method_from_instruction_offset (
234- verify_hash_hit .start (), 1
235- )
200+ config_method = self ._dnpp .method_from_instruction_offset (verify_hash_hit .start (), 1 )
236201 encrypted_config = self ._dnpp .method_body_from_method (config_method )
237202 min_config_len = self ._MIN_CONFIG_LEN_CEILING
238203 while True :
239204 try :
240- decrypted_config = self ._decrypt_and_decode_config (
241- encrypted_config , min_config_len
242- )
205+ decrypted_config = self ._decrypt_and_decode_config (encrypted_config , min_config_len )
243206 return config_method .rva , decrypted_config
244207 except Exception as e :
245208 # Reduce the minimum config length until we reach our floor
0 commit comments