/* 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; }