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.
Contents
Introduction
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 use various anti-debugging techniques to prevent reversing engineering.
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.
You can read more details about hooking technique in our Windows API hooking article.
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:
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:
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:
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
- http://www.ollydbg.de/version2.html
- http://www.codeproject.com/Articles/20084/A-More-Complete-DLL-Injection-Solution-Using-Creat
- http://codefromthe70s.org/mhook22.aspx