🐳
Swayam's Blog
LinkedinGithub
  • 🫚root@Swayam's Blog
  • 🕺About Me
  • 🛠️Projects
    • CurveLock
    • ShadowChain
  • 🐞Malware Analysis
    • Basic Malware Analysis
      • LAB Network Setup
      • Basic Static Analysis
      • Basic Dynamic Analysis
      • Advanced Dynamic Analysis
      • Advanced Static Analysis
      • Identifying Anti analysis techniques
      • Binary Patching
      • Shellcode Analysis
      • Malware.unknown.exe.Malz
      • Challenge-Sillyputty
      • Bind_shell RAT Analysis
      • Malicious Powershell Script
      • Malicious HTA(HTML Applications)
      • Phishing Excel Embedded Malware
      • Reversing Csharp And DotNET Framework
      • YARA rules
      • Automating Malware Analysis
    • MASM 64 Bit Assembly
      • Hello World Of Assembly Language
      • Computer Data Representation and Operations
      • Memory Access And Organization
      • Constants, Variables And Data Types
      • Procedures
  • 👨‍💻Malware/Exploit Development
    • Driver Development
      • Driver 101
      • Kernel Calbacks
      • Process Protection
      • Process Token Privilege
  • 📖Notes And Cheatsheets
    • OSCP / Application Security
      • OS stuff
        • Footprinting
        • Nmap
        • Shells
        • Metasploit
        • Windows Buffer Overflow
        • Windows
        • Windows Privilege Escalation
        • Linux Commands
        • Linux Privilege Escalation
        • Password Cracking
        • Pivoting And Tunneling
        • Macos
      • General Introduction
        • Basic Tools
        • Basic Networking
      • WebApps
        • Attacking Common Applications
        • Attacking Common Services
        • Broken Authentication
        • Burp Proxy
        • Common Apps
        • Command Injection
        • ffuf Fuzzing
        • File Inclusion
        • File Transfer
        • File Upload
        • Javascript Deobfuscation
        • Password Attacks
        • SQLi
        • Web attacks
        • Web Information Gathering
        • Wordpress
        • Brute Forcing
        • HTTP Curl
      • Active Directory
    • Wireless Attacks
    • Red Teaming
    • BloodHound
    • Pentesting
    • ADCS
  • 🚩CTFs
    • Google CTF
Powered by GitBook
On this page
  • Introduction
  • Anti-analysis Techniques
  • IsDebuggerPresent() API Call
  • Defeating Simple Anti-analysis
  • Summary
  1. Malware Analysis
  2. Basic Malware Analysis

Identifying Anti analysis techniques

PreviousAdvanced Static AnalysisNextBinary Patching

Last updated 1 month ago

Introduction

In the previous section, we learned the methodology for simple binary patching at the Assembly level. Binary patching allows us to alter and control the flow of execution for a malware sample. No matter what the malware sample was built to do, binary patching allows us to alter its behavior and coerce it to execute within our analysis lab.

Let’s pivot the concept of binary patching into one of its adjacent and practical use cases: defeating anti-analysis techniques.

Anti-analysis Techniques

Anti-analysis is the broad term for a multitude of techniques that malware authors use to disrupt the malware analysis process. Anti-analysis can be as general as obfuscation, where malware samples are filled with junk strings, null byte overlays, and other random detritus.

But more specifically, anti-analysis also means when a malware author puts special code in a malware sample to detect when it is being examined and deter the examination. Malware authors may code their malware to identify when it is being debugged, identify if it is in a virtual machine, and even identify if it is in a specific environment like FLARE-VM! Malware analysis is always a cat-and-mouse game when it comes to identifying and outsmarting the bad guys.

The pertinent MITRE ATT&CK Matrix items for anti-analysis include T1497 - Virtualization/Sandbox Evasion and its sub-techniques, and T1622 - Debugger Evasion, among others. Additionally, the use of execution guardrails (T1480 and its sub-techniques) can be considered anti-analysis. All of these techniques belong to the Defense Evasion tactic:

IsDebuggerPresent() API Call

Let’s begin by examining the quintessential anti-analysis technique: the IsDebuggerPresent() API call. This technique is a naïve form of anti-analysis in which the malware sample detects the presence of a debugger that is attached to its process. It will alter the flow of the program in response to the debugger and exit from the program without triggering its payload.

This technique is quite easy to detect and defeat, but it is an excellent introduction to the anti-analysis methodology and how to counter it.

Locate the executable sample in **PMAT-labs\\labs\\2-5.AntiAnalysis\\1.simpleAntiAnalysis** and copy it to the FLARE-VM Desktop. For the purpose of this lab, we will skip right to the Advanced Static Analysis portion. Open the simpleAntiAnalysis-cpp.exe binary in Cutter to begin analysis.

This sample is a 64-bit binary written in C++ and weighs in at a whopping 313KB. String analysis indicates that this sample has been cross-compiled from Linux using the Minimum GNU Compiler for Windows (mingw).

In Cutter, locate the WinMain method within the binary and double click to load into that method:

Then, switch to the Graph View:

The sample performs its main work here in this method. The first interesting thing here is the presence of the IsDebuggerPresent() API call. Let’s examine the Microsoft documentation for this API call:

The API call is quite straightforward. This returns a Win BOOL value depending on if the program detects an attached debugger.

The core of the logic of the debugger check exists in these instructions:

First, the IsDebuggerPresent() API call occurs. The specifics of how a debugger is detected is not particularly interesting for our purposes. What is interesting, however, is the return value that is loaded into EAX after this call returns. Like the documentation says, the program sets the value of EAX to 1 if a debugger is detected and sets the value of EAX to 0 if a debugger is not detected.

Note: though this binary is 64-bit, EAX is used here to perform the _TEST_ instruction. This is because in 64-bit architecture, the smaller registers can still be used if the values are small enough to fit in them. That’s why you see values moving in and out of AL, EAX, and the like.

Then the program performs a SETNE AL (SET If Not Equal To) instruction call. We have not seen this instruction before, but it is quite simple:

SETNE/SETNZ - Set if Not Equal / Set if Not Zero (386+)

Usage: SETNE dest SETNZ dest Modifies flags: none

Sets the byte in the operand to 1 if the Zero Flag is clear, otherwise sets the operand to 0.

Simply put, SETNE AL sets the value of AL (the lower bits of EAX) to 1 or 0 depending on if the Zero Flag is clear or not. Here, the program has determined if a debugger is present based on the return value of the IsDebuggerPresent() call and is now prepping the memory registers to jump to another set of instructions.

Finally, the value of AL is TESTed against itself. This is again similar to what we saw in the Binary Patching sample. This TEST instruction sets the Zero Flag to 1 or 0 depending on the contents of AL.

In the previous section on Binary Patching , we saw the JNE(Jump If Not Equal To) instruction. JNE controlled the flow of the program based on if the Zero Flag was set to 1 or 0. In this program, we have a similar instruction, which is JE [Memory Location] (Jump if Equal To). JE evaluates the Zero Flag and acts as the inverse instruction: if the Zero Flag is equal to 1, the jump is taken. If the Zero Flag is equal to 0, the jump is not taken.

In this case, the Jump here directs the program to the proper detonation of the malware, where a simple message box pops up and says that the coast is clear!

However, the opposite code path pops up a message box that identifies that a debugger is present. We can load this program into x64dbg and run the program (F9) until we see the message box:

Ok, So What Does This Actually Do?

  • The program calls IsDebuggerPresent(). If a debugger is present, a 1 is stored in EAX. Otherwise, a 0 is stored in EAX.

  • This value is TESTed against itself. The value undergoes a bitwise AND operation. Bitwise AND of a two values result in a value of 0 if the operands are both 0. If this value ends up being 0, the Zero Flag is set.

  • SETNE AL evaluates the Zero Flag. If the Zero Flag is clear, SETNE sets the value of AL to 1. In the opposite case, it sets the value of AL to 0.

  • Whatever value is placed in AL is TESTed against itself, and the Zero Flag is set to 1 or 0 again.

  • The JE [memory address] evaluates the Zero Flag and jumps to the memory location if it equals 1 and proceeds to the other code path if the Zero Flag is 0.

If a debugger is attached:

  • IsDebuggerPresent() = 1 -> EAX = 1

  • TEST EAX, EAX (bitwise AND of 1 and 1) -> 1

  • Zero Flag = 0 (Zero Flag is cleared because the TEST result was not 0)

  • SETNE AL = 1

  • TEST AL, AL -> 1

  • Zero Flag = 0

  • JE goes to "No Soup For You!"

And in the opposite case:

  • IsDebuggerPresent() = 0 -> EAX = 0

  • TEST EAX, EAX (bitwise AND of 0 and 0) -> 0

  • Zero Flag = 1 (Zero Flag is set because the TEST result was 0)

  • SETNE AL = 0

  • TEST AL, AL -> 0

  • Zero Flag = 1

  • JE goes to "Boom!"

This is the low-level equivalent of a conditional statement that would say something like:

if (isDebuggerPresent() {
MessageBox(GetForegroundWindow(), "Oh, you think you're slick, huh? I see your debugger over there. No soup for you!", "MEGASUSBRO",MB_OK);
exit (EXIT_FAILURE);
}
else {
MessageBox(GetForegroundWindow(), "No debugger detected! Cowabunga, dudes!", "COAST IS CLEAR",MB_OK);
MessageBox(GetForegroundWindow(), "Boom!", "PAYLOAD",MB_OK);
exit (EXIT_SUCCESS);
}

Note: if the minutia details here are confusing, don’t worry! Low level languages like ASM can be tough to wrap your head around and, in truth, that is the reason why high level languages even exist! Just remember that these low level instructions are really only more primitive, basic versions of the higher level code like you see above. You do not need to know exactly what every instruction is doing in order to identify and defeat anti-analysis techniques.

The crux of the matter here is this: if we can find the place where the program evaluates for the debugger and sets values as a result, we can patch or alter the instruction to get it to trigger even in the presence of a debugger. Let’s find out how!

Defeating Simple Anti-analysis

In the Binary Patching section, we used static patching in Cutter to patch out the instructions. Here, let’s use dynamic patching within the debugger itself to show an alternative method of binary patching.

In the Binary Patching section, we saved a new copy of the sample binary and overwrote an instruction to alter the code path to our liking. In the method we’ll use for this sample, we can patch the program’s logic without altering its on-disk structure. The patch will be done entirely in the running process memory.

First, let’s execute the program in a debugger and see how it functions. Exit out of Cutter and load the program into x64dbg. Run the program (F9) until the message box triggers:

Let’s find the instruction that performs the IsDebuggerPresent() check. It should be close to the start of the WinMain function, which we saw in Cutter.

Restart the program (Ctl+F2) and return to the entry point of the program. Right Click inside the CPU section and select “Search For → All Modules → String References”

In the String search panel, enter: IsDebuggerPresent

The string “IsDebuggerPresent” is passed to the LoadLibrary and GetProcAddress functions so it can be resolved and used when the program runs. This is useful to us to get us in the vicinity of where the debugger check is performed in the program. Set a breakpoint on this string reference (F2).

Return to the CPU panel and continue the execution of the program by pressing F9. We eventually hit out breakpoint:

The string reference for “IsDebuggerPresent” is currently being resolved, so we’re close to where we want to be. Continue to step through the program with F8 until you return from this call:

After returning from this call, we are here in the program (I recommend setting a breakpoint here!):

This should look familiar! TEST EAX,EAX, then SETNE AL, then TEST AL,AL, then JE to a location in the program. In fact, this is exactly what we saw earlier in Cutter:

To perform the patch, we need to change the flow of the program. Just like with static patching in the previous section, there are a few ways to do this.

Notice how when RIP is on the jump instruction, the Zero Flag is cleared (set to 0). This means that a debugger has been detected and this jump will go to the message box that says “I see your debugger over there!”

The running process memory is ours to modify to our liking. Hey, if the malware is running on our computer and we’re the administrator, who says we can’t modify the runtime values in memory? No one says that, so we will do just that! Why don’t we change the Zero Flag to a 1? That way, we will jump to the other code path!

Double click on the Zero Flag to flip it to a 1:

Now, the program proceeds to the code path that spawns two message boxes that indicate the coast is clear to run the payload. Continue execution with F8 to see the parameters for each message box get loaded into the registers and then the CALL EAX instructions which spawn the message boxes:

Even with an attached debugger, we have detonation! We have successfully identified and defeated an anti-analysis technique!

Summary

The example in this section is simple but should illustrate the point: anti-analysis measures are meant to slow down the analysis process, but if we are clever and crafty, we can counter these anti-analysis techniques.

Then, the program performs a bitwise AND of the value of EAX. We’ve seen this before in the Binary Patching section (). The bitwise AND of EAX will set the Zero Flag (ZF) to 1 or 0. This effectively means the Zero Flag is set depending on if a debugger is detected or not.

🐞
IsDebuggerPresent function (debugapi.h) - Win32 apps
https://notes.huskyhacks.dev/notes/on-patching-binaries
Virtualization/Sandbox Evasion
Debugger Evasion
Execution Guardrails