-
Notifications
You must be signed in to change notification settings - Fork 4
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
small intro notes, how to start to make it easier for people to get on board #16
Comments
@doomedraven I love this idea, and thank you very much for putting together some initial notes as someone coming to the project with a fresh perspective. I'm going to leave this Issue open and develop a feature branch associated with it to put together a In it, I will include your notes above, and I will also elaborate on a few points and add some areas that I think are most relevant for those coming in to create new decryptors. After I get a draft done, I'll ping you back for review. Thank you! |
lets call it v1, i leave a copy here while we elaborate it in private [TOC] RAT KING PARSER + DotNet handling
How it works?
RKP code - dotnetpe_payload.py Explained:
def _generate_method_list(self) -> list[DotNetPEMethod]:
method_objs = []
for idx, method in enumerate(self.dotnetpe.net.mdtables.MethodDef.rows):
method_offset = self.offset_from_rva(method.Rva)
# Parse size from flags
flags = self.data[method_offset]
method_size = 0
if flags & 3 == 2: # Tiny format
method_size = flags >> 2
elif flags & 3 == 3: # Fat format (add 12-byte header)
method_size = 12 + bytes_to_int(self.data[method_offset + 4 : method_offset + 8])
method_objs.append(
DotNetPEMethod(
method.Name.value,
method_offset,
method.Rva,
method_size,
(MDT_METHOD_DEF ^ idx) + 1, # MDT_METHOD_DEF = 0x6000000
)
)
return method_objs
# Given the offset to an instruction, reverses the instruction to its
# parent Method, optionally returning an adjacent Method using step to
# signify the direction of adjacency, and using by_token to determine
# whether to calculate adjacency by token or offset
def method_from_instruction_offset(self, ins_offset: int, step: int = 0, by_token: bool = False): # ->DotNetPEMethod:
for idx, method in enumerate(self._methods_by_offset):
if method.offset <= ins_offset < method.offset + method.size:
return (
self._methods_by_token[self._methods_by_token.index(method) + step]
if by_token
else self._methods_by_offset[idx + step]
)
raise ConfigParserException(f"Could not find method from instruction offset {hex(ins_offset)}")
How to extract config entries?
// Settings.VERSION = "gXqfa1E9yGniW4VWnl5FkeenOtkCPXVE52Tt9fhCNeAuqr6bEymGEbHpfYcw7FO6QFvadQBuujgGhJdIBeXZ/g==";
/* 0x0000C6A4 72D4310070 */ IL_0000: ldstr "gXqfa1E9yGniW4VWnl5FkeenOtkCPXVE52Tt9fhCNeAuqr6bEymGEbHpfYcw7FO6QFvadQBuujgGhJdIBeXZ/g=="
/* 0x0000C6A9 807D010004 */ IL_0005: stsfld string Quasar.Client.Config.Settings::VERSION
// Settings.HOSTS = "QXCMSAn9RQ/VBoWMf6G0O+GrLCzdhbfe4UgztC/EFgRmGjJe0GzV2Ut/Rix1phYfNFmGl4hxwOg9lUaAlgsH2CL8fiALtHoXO+qs4CAe5hA=";
/* 0x0000C6AE 7287320070 */ IL_000A: ldstr "QXCMSAn9RQ/VBoWMf6G0O+GrLCzdhbfe4UgztC/EFgRmGjJe0GzV2Ut/Rix1phYfNFmGl4hxwOg9lUaAlgsH2CL8fiALtHoXO+qs4CAe5hA="
/* 0x0000C6B3 807E010004 */ IL_000F: stsfld string Quasar.Client.Config.Settings::HOSTS
// Settings.RECONNECTDELAY = 5000;
/* 0x0000C6B8 2088130000 */ IL_0014: ldc.i4 5000
/* 0x0000C6BD 807F010004 */ IL_0019: stsfld int32 Quasar.Client.Config.Settings::RECONNECTDELAY
Example
# instead of 72D4310070 we just need to use D4310070 (removed 0x72)
def get_string_from_mdtoken(self, mdtoken: bytes):
mdtoken = struct.unpack_from("<I", mdtoken)[0] & 0xFFFFFF
return dnfile.net.user_strings.get(mdtoken).value Crypto part
_PATTERN_AES_KEY_AND_BLOCK_SIZE_AND_ALGO = re.compile(
rb"[\x06-\x09]\x20(.{4})\x6f.{4}[\x06-\x09]\x20(.{4})\x6f.{4}[\x06-\x09](.)\x6f.{4}|\x11\x01\x20(.{4})\x28.{4}\x11\x01\x20(.{4})\x6f.{4}\x11\x01(.)\x6f.{4}", re.DOTALL,
)
class EncryptedStringConfigItem2(ConfigItem):
def __init__(self) -> None:
super().__init__("encrypted string", rb"\x72(.{3}\x70)\x28.{4}\x80(.{3}\x04)")
# Returns the encrypted string's RVA
def _derive_item_value(self, enc_str_rva: bytes) -> int:
return bytes_to_int(enc_str_rva) |
i have made my small notes to make it easier to start with RKP, would be nice to add it somewhere maybe wiki, or howto.md idk :)
keeping a copy here right now so we can elaborate it better
Intro
dnfile
to handle dotnet. It does nice job to abstract everything.Where to start
How it works?
VerifyHash()
function by regex pattern. Normally is at the bottom of method which loads config entries. In case of not found, See next step.bruteforce
. RKP loops all theClasses
and gets all.cctor
methods.Once we have method(s):
config_item.py
types to extract the field names and values. If we match encrypted string, we try to brutefoce the decryption to looping all the decryptors available.Config entries
MIN_THRESHOLD_MATCH
== 3 for minimal config match,Pull Request
if you found new fork/variant with new entries.config_item.py
. See as example:class EncryptedStringConfigItem(ConfigItem):
, add your class toSUPPORTED_CONFIG_ITEMS
at the bottom and to src/rat_king_parser/config_parser/rat_config_parser.py to match it as Example:if type(item) in (config_item.EncryptedStringConfigItem, config_item.EncryptedStringConfigItem2):
The text was updated successfully, but these errors were encountered: