initterm code hiding trick

2 minute read

OverView

Probably we all know about TLS Callbacks which run early in the process before the main application code starts where a malware developer can put code there to check or do something to hide itself.

Here is another trick to do the same thing.

Analysis

I wrote a small POC for this trick that will check for a debugger and if no debugger is caught it will launch a “calculator” to prove the code execution flow control.

here is what we get with no debuggers attached.

Error

and here we can see that the main is not called yet but the debugger got caught.

Error

So let’s look at what happened in the debugger before going into source code.

have a close look at this pseudo code.

Error

here is the normal code that you will see in the environment setup before a call to main in C++ language, but in there there is an interesting call to a function called _initterm.

According to “MSDN” this is a function that takes two parameters and is described as the following…

Internal methods that walk a table of function pointers and initialize them.

The first pointer is the starting location in the table and the second pointer is the ending location.

So If we can put a function pointer to one of our functions that will be a pre-main execution.

One way I found to do that is through dynamically assigned global variables which get assigned with a return value of a function, in this case, a pointer to this function will be put in the previously mentioned “initterm” table.

Error

In our case, I have two entries for two functions initializing two global variables.

Here is the implementation for one of them which is as easy as a call to “IsDebuggerPresent”.

Error

In real life, this will be hard to get if you don’t look at the “initterm” table yourself as the code will not be as small as it’s here and not as simple as it is here.

Source Code

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

char* getname();
char* debugger();


char* check = debugger();
char* name = getname();


void _tmain(int argc, TCHAR* argv[])
{
   
    
}

char* debugger() {
    bool result = IsDebuggerPresent();
    if (result)
    {
        printf("Debugger Detected \n");
        return (char*)"failed";
    }
    else {

        printf("No debuggers (or at least you passed me) \n");
        return (char*)"pass";
    }
}


char* getname() {
    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    char* val = (char*)"Spider0x";

    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));

    wchar_t cmdline[] = L"calc.exe";

    if (check == "pass")
    {
        if (!CreateProcess(NULL,   // No module name (use command line)
            cmdline,        // Command line
            NULL,           // Process handle not inheritable
            NULL,           // Thread handle not inheritable
            FALSE,          // Set handle inheritance to FALSE
            0,              // No creation flags
            NULL,           // Use parent's environment block
            NULL,           // Use parent's starting directory 
            &si,            // Pointer to STARTUPINFO structure
            &pi)           // Pointer to PROCESS_INFORMATION structure
            )
        {
            printf("CreateProcess failed (%d).\n", GetLastError());
            return (char*)"failed";
        }
    }
    
    printf("%s", val);

    return val;
}

Conclusion

Always look for weird entries in the “initterm” table.