/*
EPOS - Heuristic Entry-point Obscuring (Virus) Scanner
and Win32. CTX.Phage disinfector
by Piotr Bania
http://pb.specialised.info
*/
#include
#include
#include
#include
#define O_CALL 0xE8
#define O_JMP 0xE9
#define TEMP_FILE_NAME "C:\_$temp.vir"
void scan_file(char *name);
bool try_disinfect(char *name, DWORD where_ctx, DWORD caller, DWORD upa, DWORD sv);
int main(int argc, char *argv[]) {
printf("---------------------------------------------------------------------------\n");
printf(" EPO-SCANNER - (c) Piotr Bania\n");
printf(" http://pb.specialised.info\n");
printf("---------------------------------------------------------------------------\n");
if (argc<2)
{
printf("[!] Usage: EPO-s.exe \n");
printf("[!] Press any key to exit.\n");
getch();
return 0;
}
printf("[+] Trying to scan: %s\n",argv[1]);
scan_file(argv[1]);
return 0;
}
void scan_file(char *name) {
HANDLE file,map;
void* mymap;
DWORD startrange = NULL, endrange = NULL, i = NULL, loc = NULL, temp_loc = NULL, upa = NULL;
DWORD where_ctx = NULL,caller = NULL, sv = NULL;
PIMAGE_DOS_HEADER pMZ = NULL;
PIMAGE_NT_HEADERS pPE = NULL;
PIMAGE_SECTION_HEADER pSH = NULL,pSHC = NULL;
char *temp_name = TEMP_FILE_NAME;
WORD sections;
int count=0;
if (!CopyFile(name,temp_name,FALSE))
{
printf("[-] Error: copying file failed - no future disinfection possible, error: %d\n",GetLastError());
}
if ((file = CreateFile(name,GENERIC_READ | FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL)) == INVALID_HANDLE_VALUE)
{
printf("[-] Error: Cannot open file - error: %d\n",GetLastError());
goto error_mode1;
}
if ((map = CreateFileMapping(file,NULL,PAGE_READWRITE | SEC_COMMIT,NULL,NULL,NULL)) == NULL)
{
printf("[-] Error: Cannot create map of file - error: %d\n",GetLastError());
goto error_mode2;
}
if ((mymap = MapViewOfFile(map,FILE_MAP_ALL_ACCESS,NULL,NULL,NULL)) == NULL)
{
printf("[-] Error: Cannot create map view of file - error: %d\n",GetLastError());
goto error_mode3;
}
pMZ=(PIMAGE_DOS_HEADER) mymap;
if (pMZ->e_magic != IMAGE_DOS_SIGNATURE)
{
printf("[-] Error: Bad MZ signature\n");
goto error_mode4;
}
pPE=(PIMAGE_NT_HEADERS) ((DWORD)mymap + pMZ->e_lfanew);
if (IsBadReadPtr((VOID*)pPE,sizeof(PIMAGE_NT_HEADERS)) == TRUE)
{
printf("[-] Error: Bad PE file\n");
goto error_mode4;
}
if (pPE->Signature != IMAGE_NT_SIGNATURE || pPE->FileHeader.NumberOfSections == NULL)
{
printf("[-] Error: Bad PE file\n");
goto error_mode4;
}
if (pPE->OptionalHeader.ImageBase <= 0 || pPE->OptionalHeader.AddressOfEntryPoint <= 0 || pPE->FileHeader.NumberOfSections <= 0)
{
printf("[-] Error: Bad PE file\n");
goto error_mode4;
}
printf("[+] Imagebase: 0x%.08x - Entrypoint: 0x%.08x (0x%.08x)\n",pPE->OptionalHeader.ImageBase,pPE->OptionalHeader.AddressOfEntryPoint,pPE->OptionalHeader.ImageBase+pPE->OptionalHeader.AddressOfEntryPoint);
sections = pPE->FileHeader.NumberOfSections;
pSH = (PIMAGE_SECTION_HEADER)((DWORD)mymap+pMZ->e_lfanew + sizeof(IMAGE_NT_HEADERS));
while (sections != 0)
{
if (IsBadReadPtr(&pSH,sizeof(PIMAGE_SECTION_HEADER)) == TRUE)
{
printf("[-] Error: Bad PE file\n");
goto error_mode4;
}
char *secname=(char *) pSH->Name;
if (secname == NULL) strcpy(secname,"NONAME");
startrange=(DWORD) pSH->VirtualAddress + pPE->OptionalHeader.ImageBase;
endrange=(DWORD) startrange + pSH->Misc.VirtualSize;
if (startrange <=0 || startrange <= pPE->OptionalHeader.ImageBase || endrange <=0 || pPE->OptionalHeader.ImageBase <= 0 || pSH->Misc.PhysicalAddress < 0 || pSH->SizeOfRawData < 0)
{
printf("[-] Error: The %s section is broken\n",secname);
goto error_mode4;
}
if (pSH->VirtualAddress <= pPE->OptionalHeader.AddressOfEntryPoint && pPE->OptionalHeader.AddressOfEntryPoint < pSH->VirtualAddress + pSH->Misc.VirtualSize)
{
printf("[+] Checking call/jump requests from %s section (EP)\n",secname);
pSHC = pSH;
}
pSH++;
sections--;
}
pSH--;
if (pSHC == NULL)
{
printf("[-] Error: invalid entrypoint\n");
goto error_mode4;
}
printf("[+] Starting heuristics scan on %s section...\n\n",pSHC->Name);
if (pSHC == pSH)
{
printf("[!] Alert: Entrypoint points to last section (%s) -> 0x%.08x\n",pSH->Name,pPE->OptionalHeader.AddressOfEntryPoint + pPE->OptionalHeader.ImageBase);
printf("[!] Alert: The file may be infected!\n");
printf("[+] No deep-scan action was performed\n");
goto error_mode4;
}
printf("[+] Starting from offset: 0x%.08x\n",pPE->OptionalHeader.ImageBase + pSHC->VirtualAddress);
for (i = 0; (i != pSHC->SizeOfRawData); i++)
{
loc = (DWORD)((DWORD)mymap + pSHC->PointerToRawData) + i;
if ((*(BYTE*)loc) == O_CALL || (*(BYTE*)loc) == O_JMP )
{
loc++;
temp_loc = (DWORD)((DWORD)pSHC->VirtualAddress + i + (*(DWORD*)loc)) + 5;
if (temp_loc >= pSH->VirtualAddress && temp_loc <= pSH->VirtualAddress + pSH->Misc.VirtualSize)
{
printf("[!] Alert: Detected request to %s(0x%.08x) section at: 0x%.08x\n",pSH->Name,pPE->OptionalHeader.ImageBase + temp_loc, pSHC->VirtualAddress + pPE->OptionalHeader.ImageBase + i);
if (where_ctx == NULL)
{
where_ctx = (DWORD)(pPE->OptionalHeader.ImageBase + temp_loc);
caller = (DWORD)(pSHC->VirtualAddress + pPE->OptionalHeader.ImageBase + i);
upa = (DWORD)(pSH->VirtualAddress + pPE->OptionalHeader.ImageBase);
sv = loc - 1;
}
count++;
}
loc--;
}
}
printf("[+] Scan finished, %d suspected instruction(s) found.\n",count);
if (count != 0)
{
printf("[!] Warning: the file may be infected!\n");
printf("\n[?] Do you want to try dis-infect the file?\n");
printf("[?] Warning: the file may be executed if this is not the CTX.Phage\n");
printf(" infection.\n");
printf("[?] Disinfect: (y)es / (n)o ? \n");
if (getch() == 'y') try_disinfect(name, where_ctx, caller, upa, sv);
}
error_mode4:
UnmapViewOfFile(mymap);
error_mode3:
CloseHandle(map);
error_mode2:
CloseHandle(file);
error_mode1:
DeleteFile(temp_name);
}
bool try_disinfect(char *name, DWORD where_ctx, DWORD caller, DWORD upa, DWORD sv) {
STARTUPINFO si;
PROCESS_INFORMATION pi;
CONTEXT tc;
DEBUG_EVENT de;
DWORD stack_v = NULL, _GetProcAddress = NULL, oldp;
unsigned char patch[4] = { 0x90, 0x90, 0xCC };
unsigned char ctx_sig[15] = { 0x6A, 0x00, 0x6A, 0x05, 0xE8, 0x05, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90, 0x90, 0x90, 0x50 };
unsigned char ctx_fly[15];
char *temp_name = TEMP_FILE_NAME;
int fe=NULL, found=NULL;
_GetProcAddress = (DWORD) GetProcAddress(LoadLibrary("KERNEL32.DLL"), "GetProcAddress");
GetStartupInfo(&si);
if (!CreateProcess(NULL,temp_name,NULL,NULL,FALSE,DEBUG_PROCESS + DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &si, &pi))
{
printf("[-] Error: cannot create process, error: %d\n",GetLastError());
goto error_di;
}
printf("\n[+] Process created, pid=0x%.08x\n",pi.dwProcessId);
printf("[+] Starting emulation engine...\n");
while (1)
{
WaitForDebugEvent(&de,INFINITE);
if (de.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) {
printf("[!] Error: ups process exited...\n");
goto error_term;
}
if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
{
if (de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
if (de.u.Exception.dwFirstChance == TRUE)
{
printf("[+] Exception occured at: 0x%.08x, passing to program.\n",de.u.Exception.ExceptionRecord.ExceptionAddress);
ContinueDebugEvent(de.dwProcessId,de.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);
}
else
{
printf("[-] Hard error occured, terminating the program\n");
printf("[-] Disinfecting failed\n");
goto error_term;
}
}
if (de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT)
{
if (fe == NULL)
{
fe = 1;
printf("[+] Reached break point at 0x%.08x\n",de.u.Exception.ExceptionRecord.ExceptionAddress);
printf("[+] Modifing 4 bytes at host stack\n");
tc.ContextFlags = CONTEXT_CONTROL;
if (!GetThreadContext(pi.hThread, &tc))
{
printf("[-] Failed to get thread context, error: %d\n",GetLastError());
printf("[-] Disinfecting failed\n");
goto error_term;
}
ReadProcessMemory(pi.hProcess, (void*)tc.Esp, &stack_v,4,NULL);
if (stack_v == NULL)
{
printf("[-] Error: reading from stack failed\n");
printf("[-] Disinfecting failed\n");
goto error_term;
}
tc.Esp = tc.Esp - 4;
caller += 5;
if (!WriteProcessMemory(pi.hProcess, (void*)tc.Esp, &caller, 4, NULL))
{
printf("[-] Error: writing to stack failed\n");
printf("[-] Disinfecting failed\n");
goto error_term;
}
printf("[+] Stack modified, 0x%.08x added caller -> 0x%.08x\n",tc.Esp, caller);
printf("[+] Redirecting EIP to 0x%.08x...\n",where_ctx);
tc.Eip = where_ctx;
if (!SetThreadContext(pi.hThread, &tc))
{
printf("[-] Failed to set thread context, error: %d\n",GetLastError());
printf("[-] Disinfecting failed\n");
goto error_term;
}
VirtualProtectEx(pi.hProcess, (void*) _GetProcAddress, sizeof(patch), PAGE_READWRITE, &oldp);
WriteProcessMemory(pi.hProcess, (void*) _GetProcAddress, &patch, sizeof(patch), NULL);
VirtualProtectEx(pi.hProcess, (void*) _GetProcAddress, sizeof(patch), oldp, &oldp);
printf("[+] Placed breaker at 0x%.08x\n",_GetProcAddress);
ContinueDebugEvent(de.dwProcessId,de.dwThreadId,DBG_CONTINUE);
}
if ((DWORD) de.u.Exception.ExceptionRecord.ExceptionAddress > _GetProcAddress && (DWORD) de.u.Exception.ExceptionRecord.ExceptionAddress < _GetProcAddress + sizeof(patch))
{
printf("[+] Virus reached the breaker at 0x%.08x\n",de.u.Exception.ExceptionRecord.ExceptionAddress);
tc.ContextFlags = CONTEXT_CONTROL;
if (!GetThreadContext(pi.hThread, &tc))
{
printf("[-] Failed to get thread context, error: %d\n",GetLastError());
printf("[-] Disinfecting failed\n");
goto error_term;
}
ReadProcessMemory(pi.hProcess, (void*)tc.Esp, &stack_v, 4, NULL);
printf("[+] Virus request captured from 0x%.08x\n",stack_v);
printf("[+] Scanning backwards to 0x%.08x\n",upa);
while (1)
{
if (!ReadProcessMemory(pi.hProcess, (void*)stack_v, &ctx_fly, sizeof(ctx_sig), NULL)) break;
if (stack_v <= upa) break;
found = 1;
for (int ii=0; ii < sizeof(ctx_sig); ii++)
{
if (ctx_sig[ii] != ctx_fly[ii])
{
if (ctx_sig[ii] != 0x90)
{
found = 0;
break;
}
}
}
if (found == 1)
{
printf("[+] Orginal bytes were found at 0x%.08x\n",stack_v + 9);
printf("[!] Repairing the broken instruction.\n");
ReadProcessMemory(pi.hProcess, (void*)(stack_v + 9), (void*) sv, 5, NULL);
printf("[!] The file was disinfected!\n");
getch();
goto error_term;
}
stack_v--;
}
if (found == 0)
{
printf("[-] Error: no signature was found.\n");
printf("[-] Disinfecting failed\n");
goto error_term;
}
goto error_term;
}
}
}
ContinueDebugEvent(de.dwProcessId,de.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);
}
error_term:
TerminateProcess(pi.hProcess,NULL);
error_di:
return TRUE;
}