Detecting Suspicious API Usage with YARA Rules

Published: 2023-04-07
Last Updated: 2023-04-07 05:45:16 UTC
by Xavier Mertens (Version: 1)
1 comment(s)

YARA[1] is a beautiful tool for malware researchers and incident responders. No need to present it again. It became a standard tool to add to your arsenal. While teaching FOR610 (Malware Analysis & Reverse Engineering), a student asked me how to detect specific API calls with dangerous parameters during the triage phase. This phase will help you quickly assess the malware sample and help you decide how to perform the following steps.

A classic suspicious API call/parameter combo is VirtualAlloc() with the value 0x40 passed as flProtect parameter. This value corresponds to PAGE_EXECUTE_READWRITE[2]. If you see this, it means that the program will allocate some new memory that will contain executable code. This is a typical step to load a shell code in memory and execute it.

In the example below, the malware uses this technique, and we can detect the call to VirtualAlloc():

remnux@remnux:~$ capa -vv WinHost32.exe?
...
allocate RWX memory (2 matches)
namespace  host-interaction/process/inject
author     moritz.raabe@fireeye.com
scope      basic block
mbc        Memory::Allocate Memory [C0007]
examples   Practical Malware Analysis Lab 03-03.exe_:0x4010EA, 563653399B82CD443F120ECEFF836EA3678D4CF11D9B351BB737573C2D856299:0x140001ABA
basic block @ 0x401000
  and:
    match: allocate memory @ 0x401000
      or:
        api: kernel32.VirtualAlloc @ 0x401077
    number: 0x40 = PAGE_EXECUTE_READWRITE @ 0x401068
basic block @ 0x4021EE
  and:
    match: allocate memory @ 0x4021EE
      or:
        api: kernel32.VirtualAllocEx @ 0x40220D
    number: 0x40 = PAGE_EXECUTE_READWRITE @ 0x402203
...

They are two calls to VirtualAlloc. The first one is located at 0x401000:

remnux@remnux:~$ objdump -d WinHost32.exe

WinHost32.exe:     file format pei-i386

Disassembly of section .text:

00401000 <.text>:
...
  401068:       6a 40                   push   0x40
  40106a:       68 00 30 00 00          push   0x3000
  40106f:       50                      push   eax
  401070:       6a 00                   push   0x0
  401072:       a3 94 ec 40 00          mov    ds:0x40ec94,eax
  401077:       ff 15 b8 b1 40 00       call   DWORD PTR ds:0x40b1b8
...

VirtualAlloc() expects four parameters. In a 32bits architecture, they are passed on the stack from write to left. The last one (flProtect) is pushed on the stack at 0x401068. How to read this code:

VirtualAlloc(0, EAX_value, 0x3000, 0x40)

How to detect this with the help of a YARA? Here is a very simple rule:

rule VirtualAlloc40
{
    meta:
        description = “Simple rule to detect PAGE_EXECUTE_READWRITE memory allocation”
    strings:
        $hex_string = { 6A 40 68 00 30 00 00 [5-15] (FF 15 | E8 ) }

    condition:
        $hex_string
}

I’m looking for the following code (see the objdump output above)

  • push a byte (0x6A) on the stack: 0x40
  • push a dword (0x68) on the stack: 0x00003000
  • a suite of 5 to 10 bytes (because the remaining parameters may be something else)
  • call an address (where VirtualAlloc is loaded)

This rule gave me good results, but it remains basic. Indeed, it won’t detect simple obfuscation like this:

mov    ecx, 0x40
push   ecx
push   0x3000
...

If you've ideas to improve this simple rule or another interesting API call/parameter combination (example: CreateProcess / SUSPENDED_MODE), please share them with us!

[1] https://yara.readthedocs.io/en/latest/
[2] https://learn.microsoft.com/en-us/windows/win32/Memory/memory-protection-constants

Xavier Mertens (@xme)
Xameco
Senior ISC Handler - Freelance Cyber Security Consultant
PGP Key

1 comment(s)

Comments

It's a good attempt, but I don't think Yara is the optimal choice. Although it can be used, its generalization ability may be weak and it has many limitations. capa is a better one.

Diary Archives