Click here to Skip to main content
Click here to Skip to main content

Anti-Debug Time Plugin for OllyDbg

By , , 3 Jul 2013
Rate this:
Please Sign up or sign in to vote.

Introduction

In this article, I will tell you how to write an anti-debug plugin for OllyDbg v. 2.01. The task is to prevent the application being debugged from detecting the debugger.

We’ll consider two steps:

  • Writing the plugin,
  • Writing the auxiliary module for plugin.

This article is intended for people experienced in C++ and dll writing.

General Information

Modern computer programs are more complex in writing and more difficult for reversing. Serious programs have various means of protection against debugging. It prevents application reversing. There are a number of various approaches, like Debug Blocker, Nanomites, others.

Measuring time to identify that an application is being debugged becomes the widespread practice lately. The OllyDbg has the «HideOD» and «Hide Debugger» anti-debug plugins, which have no possibility to hide actual time. This causes difficulties in application reversing.

Let’s consider the system of debugger identification. The debuggers are capable of making breakpoints in code. In this case the operation of the program is suspended. The program can detect such stopping by monitoring the system time. If there is a too long pause between the instructions – most likely the program has been stopped for analysis.


const int g_doSmthExecutionTime = 1050;
 
void DoSmth()
{
   Sleep(1000);
}
int main(int argc, char* argv[])
{
   SYSTEMTIME sysTimeStart;
   SYSTEMTIME sysTimeEnd;
   FILETIME timeStart, timeEnd;
   
   GetSystemTime(&sysTimeStart);
 
   DoSmth();
 
   GetSystemTime(&sysTimeEnd);
 
   SystemTimeToFileTime(&sysTimeStart, &timeStart);
   SystemTimeToFileTime(&sysTimeEnd, &timeEnd);
 
   double timeExecution = (timeEnd.dwLowDateTime - timeStart.dwLowDateTime) / 10000.0;
 
   if (timeExecution < g_doSmthExecutionTime)
   {
      std::cout << "Debugger not present";
   }
   else
   {
      std::cout << "Debugger present";
   }
   return 0;
} 

This example measures the time of function execution, which is then compared to standard. This standard equals to the maximal normal function execution time.

The following functions can be used to measure time:

  • GetSystemTime;
  • GetSystemTimeAsFileTime;
  • QueryPerformanceCounter/QueryPerformanceFrequency;
  • GetTickCount;
  • GetTickCount64.

Hiding of OllyDbg by execution time substitutes custom time value for the original time, returned by the function. This is implemented by means of function hooks, as such a solution is painless and unnoticeable by an application.

I.e. the pointer of the original function is replaced with the custom function with custom implementation, where the value is substituted. This implementation is written in dll loaded into the target process. dll includes exported functions – Start and Stop. Start sets up the hooks, and Stop removes them.

3. Plugin Operation Principle

The application is split into two parts:

  • Main dll (AntiDebugTimePlugin)
  • Auxiliary dll (Plugin)

The main dll is the plugin that interacts with OllyDbg and has the interface for interaction with auxiliary dll. It loads the auxiliary dll into the address space of the target process to perform the required actions.

The auxiliary dll has the code for setting and removing hooks. It also has the altered logic of functions that work with time. This dll replaces the true value of original function with the value set in the main dll. Thus the time for measurement is substituted, which prevents the debugger from being detected.

The plugin operation principle is as follows:

im1

We can see from the scheme how OllyDbg loads the plugin. The plugin writes the values to the registry for interaction with the auxiliary dll. The plugin embeds the auxiliary dll into the process launched by OllyDbg.

Development

Let’s get to the development itself.

Plugin Development

OllyDbg performs a search in dll located in the current folder and calls the exported functions it requires. Thus, after the function is found, OllyDbg loads it and adds the corresponding menu.

First, OllyDbg calls  ODBG2_Pluginquery function. Inside this function, we check the OllyDbg version. If the version is suitable, we allow the plugin to be loaded into a process.

extc int __cdecl ODBG2_Pluginquery(int ollydbgversion,
                                   ulong *features,
                                   wchar_t pluginname[SHORTNAME],
                                   wchar_t pluginversion[SHORTNAME])
{
    if (ollydbgversion < 201)
    {
        return 0;
    }
    wcscpy_s(pluginname, SHORTNAME, g_pluginName);
    wcscpy_s(pluginversion, SHORTNAME, g_version);
    return PLUGIN_VERSION;
}

Then we need to initialize the variables before execution. We need to initialize a variable to create a window in OllyDbg style and set a range of values for auxiliary dll.

extc int __cdecl ODBG2_Plugininit(void)
{
    StrcopyW(g_table.name, SHORTNAME ,g_pluginName);
    g_table.mode = TABLE_SAVEALL;
    g_table.bar.visible = 1;
    g_table.bar.nbar = 1;
    g_table.tabfunc = SetRangeWND;
    g_table.custommode = 0;
    g_table.customdata = NULL;
    g_table.updatefunc = NULL;
    g_table.drawfunc = reinterpret_cast<DRAWFUNC*>(SetRangeDraw);
    g_table.tableselfunc = NULL;
    g_table.menu = NULL;
    // Init registry value
    SetRange(g_defaultMinValue, g_defaultMaxValue);
    // Report success.
    return 0;
}  

Now we need to create a menu for OllyDbg to draw it in its own menu.

To do this, we create an array with the description of all menu items and their processing functions.

 
static t_menu mainmenu[] = {
    { L"Set range",
       L"Set min-max value of time",
       K_NONE, SetRangeMenu, NULL, 0 },
    { L"On",
       L"Hide OllyDbg in debugging process which uses functions time for defines OllyDbg",
       K_NONE, HideOllyDbgMenu, NULL, 0 },
    { L"|About",
       L"About Antidebug time-plugin",
       K_NONE, AboutMenu, NULL, 0 },
    { NULL, NULL, K_NONE, NULL, NULL, 0 }
};

The function for returning menu for OllyDbg.

extc t_menu* __cdecl ODBG2_Pluginmenu(wchar_t *type)
{
    if (wcscmp(type, PWM_MAIN) == 0 ||
       (wcscmp(type, PWM_DISASM) == 0)
    {
        return mainmenu;
    }
    return NULL;
}

Now let’s consider the basic functions for work with menu.

The first item is intended for creation of the window, in which the range is entered.

int SetRangeMenu(t_table *pt, wchar_t *name, ulong index, int mode)
{
    if (mode == MENU_VERIFY)
        return MENU_NORMAL; 
    if (mode == MENU_EXECUTE)
    {
        if (g_table.hw == NULL)
        {
            Createtablewindow(&g_table, 0, g_table.bar.nbar, NULL, L"ICO_D", g_wndRangeName);
            SendMessage(g_table.hw, WM_CREATE, 0, 0);
        }
        else
        {
            Activatetablewindow(&g_table);
        }
        return MENU_NOREDRAW;
    }
    return MENU_ABSENT;
}

The Createtablewindow is the function for creating a window in OllyDbg style. In our case – the window without a table, but with edit elements is used.

The second item turns the plugin on and off.

int HideOllyDbgMenu(t_table *pt, wchar_t *name, ulong index, int mode)
{
    if (mode == MENU_VERIFY)
    {
        return (g_isNeedHooking ? MENU_CHECKED : MENU_NORMAL);
    }
    else if (mode == MENU_EXECUTE)
    {
        g_isNeedHooking = !g_isNeedHooking;
        return MENU_REDRAW;
    }
    return MENU_ABSENT;
}

As the second item is checked, we return the menu item state depending on the flag state.

The only thing left is to subscribe to OllyDbg events to receive the information on the process being loaded.

extc void __cdecl ODBG2_Pluginnotify(int code, void *data, ulong parm1, ulong parm2)
{
    if (PN_NEWPROC == code)
    {
        CThreadStateGuard threadState;
        if (IsNeedHooking())
        {
            // Hooking
            g_processID = parm1;
            processState::Suspend(g_processID);
            ::CloseHandle(::CreateThread(NULL, 0, SetHookThread, NULL, NULL, NULL));
        }
    }
    else if(PN_ENDPROC == code)
    {
        CThreadStateGuard threadState;
        // UnHookHooking
        g_processID = parm1;
        ::CloseHandle(::CreateThread(NULL, 0, UnHookThread, NULL, NULL, NULL));
    }
    else if (PN_ENDTHR == code)
    {
        processState::Resume(g_processID);
    }
}

There are 3 interesting events in this function: the emergence of a new process, process termination, and  thread termination.

After the notification about a new process is received, we load the auxiliary dll into the process address space.

Before the process is terminated, we remove the hooks set earlier.

In order for auxiliary dll to work properly, we need to set hooks before the process starts to execute the code. To do this, first, we freeze the main thread of the process so that no code is executed. After the process is loaded into OllyDbg completely, we unfreeze the process, and then the functions from auxiliary dll are executed instead of the original functions.

Starting with Windows Vista, it became more complicated to execute custom code in another process by means of CreateRemoteThread. The complication is that with each new start of the same process the functions are loaded to different adresses each time. Thus it’s impossible to pass LoadLibrary to CreateRemoteThread as parameter, so that it loads the auxiliary dll in another process.

The solution is to allocate memory in another process and write  code for execution bit-by-bit. I took the CodeProject code as a basis.

Auxiliary dll for plugin

This dll hooks such functions:

  • GetSystemTime
  • GetSystemTimeAsFileTime
  • QueryPerformanceCounter
  • QueryPerformanceFrequency
  • GetTickCount
  • GetTickCount64

It has two exported functions used by the main dll to set and remove hooks.

extern "C" __declspec(dllexport) void Start(void);
extern "C" __declspec(dllexport) void Stop(void);

The HookFunction function is used to set hooks. It generates an exception in case of an error and checks if a function has already been hooked.

FARPROC GetHookedFunctionAddr(const wchar_t* functionModuleName, const char* functionName)
{
    HMODULE hm = GetModuleHandleW(functionModuleName);
    return GetProcAddress(hm, functionName);
}
template <typename FunctionType>
bool HookFunction(FunctionType& originalFunction, 
                  void* hookFunction, 
                  const wchar_t* functionModuleName, 
                  const char* functionName,
                  bool isFunctionHooked)
{
    try
    {
        if (!originalFunction && !isFunctionHooked)
        {
            originalFunction = reinterpret_cast<FunctionType>(GetHookedFunctionAddr(functionModuleName, 
functionName));
            if (originalFunction)
            {
                if (!Mhook_SetHook(reinterpret_cast<void**>(&originalFunction), hookFunction))
                {
                    throw std::runtime_error("not successfully hooked"); 
                }
            }
            return true;
        }
        return true;
    }
    catch(const std::exception&: ex)
    {
        return false;
    }
}

To simplify the hook setting, let’s wrap this function into a macros with  function name and the name of the module, it is located in, as input parameters.

#define HOOK_FUNCTION(FUNCTION_NAME, DLL_NAME)\
    (HookFunction(g_original_##FUNCTION_NAME, \
    HookFor_##FUNCTION_NAME, \
    ##DLL_NAME,\
    ##FUNCTION_NAME##_NAME,\
    is_hook_set_for_##FUNCTION_NAME##)) \

Then we set a flag to indicate the function has been hooked.

#define COMMIT_FUNCTION(FUNCTION_NAME) \
    (is_hook_set_for_##FUNCTION_NAME = true)

To unhook the function, we use macros as well:

#define UNHOOK_FUNCTION(FUNCTION_NAME) \
    if (!Mhook_Unhook(reinterpret_cast<PVOID*>(&g_original_##FUNCTION_NAME)) ) \
    { \
        throw std::runtime_error("Can't unhook "#FUNCTION_NAME" function");\
    } \
    else \
    { \
        g_original_##FUNCTION_NAME = NULL; \
        is_hook_set_for_##FUNCTION_NAME = false; \
 } \

Now we use the macros to set hooks:

extern "C" __declspec(dllexport) void Start(void)
{
    try
    {
        ::srand(time(NULL));
        OpenRange(g_minRangeValue, g_maxRangeValue);
        const std::wstring kernelName = L"kernel32.dll";
        HOOK_FUNCTION(GetSystemTime, kernelName.c_str());
        COMMIT_FUNCTION(GetSystemTime);
        HOOK_FUNCTION(GetSystemTimeAsFileTime, kernelName.c_str());
        COMMIT_FUNCTION(GetSystemTimeAsFileTime);
        ...
    }
    catch(const std::exception& exp)
    {
        exp;
    }
}

To remove hooks:

extern "C" __declspec(dllexport) void Stop(void)
{
    try
    {
        UNHOOK_FUNCTION(GetSystemTime);
        UNHOOK_FUNCTION(QueryPerformanceCounter);
        UNHOOK_FUNCTION(QueryPerformanceFrequency);
        UNHOOK_FUNCTION(GetTickCount);
    }
    catch(const std::exception& exp)
    {
        exp;
    }
}

Hooked Function Operation Principle

Let’s consider an example for one function, as all functions have the same logic.

BOOL __stdcall HookFor_QueryPerformanceCounter(_Out_ LARGE_INTEGER *lpPerformanceCount)
{  
    LARGE_INTEGER perfomanceCount = {0};
 
    std::wstringstream str;
    if (g_isFirstCallingQueryPerformanceCounter)
    {
        perfomanceCount.LowPart = static_cast<WORD>(g_minRangeValue);
    }
    else
    {
        perfomanceCount.LowPart = static_cast<WORD>(RandValue(g_minRangeValue, g_maxRangeValue));
       perfomanceCount.LowPart += static_cast<WORD>(g_minRangeValue);
    }
 
    perfomanceCount.QuadPart = static_cast<LONGLONG>(perfomanceCount.LowPart);
 
    g_isFirstCallingQueryPerformanceCounter = !g_isFirstCallingQueryPerformanceCounter;
 
    *lpPerformanceCount = perfomanceCount;
 
    return g_original_QueryPerformanceCounter(&perfomanceCount);
}

First this function returns the minimum value from the range set in the main dll, then it returns a random value from the same range (min < number < max).

I.e. if the execution time is calculated (the difference between the end and the beginning of the execution), the application will always receive a value in this range: min < number < max.

Plugin in Action

To perform testing, let’s write an application that returns the difference between the beginning of the Sleep function work and its end. We’ll take 1000 milliseconds for the delay. We show this value in MessageBox.

int _tmain(int argc, _TCHAR* argv[])
{
    while(1)
    {
        SYSTEMTIME lpSysTimeStart;
        SYSTEMTIME lpSysTimeEnd;
 
        FILETIME tmEnd,tmStart;
 
        GetSystemTime(&lpSysTimeStart);
 
        Sleep(1000);
 
        GetSystemTime(&lpSysTimeEnd);
 
        SystemTimeToFileTime(&lpSysTimeStart, &tmStart);
        SystemTimeToFileTime(&lpSysTimeEnd, &tmEnd);
 
        int value = (tmEnd.dwLowDateTime - tmStart.dwLowDateTime) / 10000;
        std::wstringstream stream;
        stream << value;
 
        MessageBox(NULL, stream.str().c_str(), L"Testing", 0);
    }
    return 0;
}

Here is the result of application work:

im2

Now let’s set up the range of values in plugin. Let’s take the range between 2000 and 3000 milliseconds as an example. Now we turn the plugin on and load the process being tested to OllyDbg.

Here is the result:

im3

im4

As you can see from the test example above, the value from the indicated range is returned.

Recommendations on Time Setup

The values are set in milliseconds. The milliseconds threshold below 10 decreases accuracy.

The values may be different, but they depend on the values checked in other program. You may start with a wide range and then gradually decrease it until the application execution begins.

Issues

There is an interesting situation: if you take an application that does not use functions connected with time, it calls GetTickCount, QueryPerformanceCounter, and QueryPerformanceFrequency one way or another. This may sometimes lead to results being slightly different from the expected ones.

The application was developed for x86 platform, as there is OllyDbg only for x32 version, and there is no OllyDbg x64 at the moment of finishing this article.

Summary

We considered the writing of the plugin for OllyDbg 2.01 in this article. We also wrote the plugin that prevents OllyDbg from being detected by function execution time.

This plugin helps in code reversing, if there are functions that detect OllyDbg by measuring time. The code with such functions embedded may close OllyDbg prematurely or may not be executed at all.

This article provides the plugin source code, which you can compile and use.

9. Possible Future Improvements

The auxiliary dll, which sets hooks, is injected into the process during its loading to OllyDbg. However it unloads from it either at application closing or after initiating the restart of the debugging application in OllyDbg.

OllyDbg can detach from an application without stopping the application’s work. If in addition the auxiliary dll was loaded, it will not be unloaded from the process.

The unloading of the dll after pressing “Detach” could be an improvement to this dll’s principle of operation. This would give an application the possibility to continue work in a normal mode.

References


  1. http://www.ollydbg.de/version2.html
  2. http://www.codeproject.com/Articles/20084/A-More-Complete-DLL-Injection-Solution-Using-Creat
  3. http://codefromthe70s.org/mhook22.aspx

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Authors

Apriorit Inc
Apriorit Inc.
Ukraine Ukraine
ApriorIT is a Software Research and Development company that works in advanced knowledge-intensive scopes.
 
Company offers integrated research&development services for the software projects in such directions as Corporate Security, Remote Control, Mobile Development, Embedded Systems, Virtualization, Drivers and others.
 
Official site http://www.apriorit.com
Group type: Organisation

30 members


prepodobniy

United States United States
No Biography provided

Comments and Discussions

 
QuestionHow can I use it? [modified] Pinmemberrafaelcn23-Mar-14 18:32 
GeneralMy vote of 5 Pinmemberenhzflep3-Jul-13 17:02 
QuestionAllowing for Reverse Engineering... PinmemberWehrmann3-Jul-13 3:12 
AnswerRe: Allowing for Reverse Engineering... Pinmemberenhzflep3-Jul-13 16:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140415.2 | Last Updated 3 Jul 2013
Article Copyright 2013 by Apriorit Inc, prepodobniy
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid