bool IATSearch::findIATAdvanced( DWORD_PTR startAddress, DWORD_PTR* addressIAT, DWORD* sizeIAT ) { BYTE *dataBuffer; DWORD_PTR baseAddress; SIZE_T memorySize; findExecutableMemoryPagesByStartAddress(startAddress, &baseAddress, &memorySize); if (memorySize == 0) return false; dataBuffer = new BYTE[memorySize]; if (!readMemoryFromProcess((DWORD_PTR)baseAddress, memorySize,dataBuffer)) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"findAPIAddressInIAT2 :: error reading memory"); #endif return false; } std::set<DWORD_PTR> iatPointers; DWORD_PTR next; BYTE * tempBuf = dataBuffer; while(decomposeMemory(tempBuf, memorySize, (DWORD_PTR)baseAddress) && decomposerInstructionsCount != 0) { findIATPointers(iatPointers); next = (DWORD_PTR)(decomposerResult[decomposerInstructionsCount - 1].addr - baseAddress); next += decomposerResult[decomposerInstructionsCount - 1].size; // Advance ptr and recalc offset. tempBuf += next; if (memorySize <= next) { break; } memorySize -= next; baseAddress += next; } if (iatPointers.size() == 0) return false; filterIATPointersList(iatPointers); *addressIAT = *(iatPointers.begin()); *sizeIAT = (DWORD)(*(--iatPointers.end()) - *(iatPointers.begin()) + sizeof(DWORD_PTR)); //Scylla::windowLog.log(L"IAT Search Advanced: Found %d (0x%X) possible IAT entries.", iatPointers.size(), iatPointers.size()); //Scylla::windowLog.log(L"IAT Search Advanced: Possible IAT first " PRINTF_DWORD_PTR_FULL L" last " PRINTF_DWORD_PTR_FULL L" entry.", *(iatPointers.begin()), *(--iatPointers.end())); delete [] dataBuffer; return true; }
void runPlugin(static HANDLE hProcess, PUNRESOLVED_IMPORT unresolvedImport, unsigned int eip){ DWORD_PTR invalidApiAddress = 0; INSTRUCTION inst; int max_instruction_size = sizeof(UINT8)*15; LPVOID instruction_buffer = (LPVOID)malloc(max_instruction_size); int instruction_size; char buffer[200]; while (unresolvedImport->ImportTableAddressPointer != 0){ //last element is a nulled struct printf("Unresolved : 0x%08x\n", unresolvedImport->InvalidApiAddress); invalidApiAddress = unresolvedImport->InvalidApiAddress; readMemoryFromProcess(hProcess, invalidApiAddress, max_instruction_size, instruction_buffer); instruction_size = get_instruction(&inst, (BYTE *)instruction_buffer, MODE_32); get_instruction_string(&inst, FORMAT_ATT, 0, buffer, sizeof(buffer)); printf("INS: %s\n",buffer); if(inst.type == INSTRUCTION_TYPE_PUSH){ //pushl $0x770fdfa4 char *pch = strstr (buffer,"0x"); //printf("ADDRESS: %s\n",pch); unsigned int correct_address = (unsigned int)strtoul(pch,NULL,16); printf("ADDRESS: %08x\n",correct_address); bool res = writeMemoryToProcess(hProcess, (DWORD_PTR)(unresolvedImport->ImportTableAddressPointer), sizeof(correct_address), &correct_address); printf("writeMemoryToProcess result %d\n" , res); }else{ if(inst.type == INSTRUCTION_TYPE_JMP){ // jmp 0x73d3673d //printf("ADDRESS: %s\n",pch); unsigned int correct_address = ( (unsigned int)strtoul(strstr(buffer, "jmp") + 4 + 2, NULL, 16)) + invalidApiAddress; //printf("ADDRESS: %08x\n",correct_address); bool res = writeMemoryToProcess(hProcess, (DWORD_PTR)(unresolvedImport->ImportTableAddressPointer), sizeof(correct_address), &correct_address); //printf("writeMemoryToProcess result %d\n" , res); } } //if libdasm fails to recognize the insruction bypass this instruction if(instruction_size == 0){ invalidApiAddress = invalidApiAddress + 1; continue; } unresolvedImport++; //next pointer to struct } }
DWORD_PTR IATSearch::findAPIAddressInIAT(DWORD_PTR startAddress) { const size_t MEMORY_READ_SIZE = 200; BYTE dataBuffer[MEMORY_READ_SIZE]; DWORD_PTR iatPointer = 0; int counter = 0; // to detect stolen api memoryAddress = 0; memorySize = 0; do { counter++; if (!readMemoryFromProcess(startAddress, sizeof(dataBuffer), dataBuffer)) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"findAPIAddressInIAT :: error reading memory " PRINTF_DWORD_PTR_FULL, startAddress); #endif return 0; } if (decomposeMemory(dataBuffer, sizeof(dataBuffer), startAddress)) { iatPointer = findIATPointer(); if (iatPointer) { if (isIATPointerValid(iatPointer, true)) { return iatPointer; } } } startAddress = findNextFunctionAddress(); //printf("startAddress %08X\n",startAddress); } while (startAddress != 0 && counter != 8); return 0; }
bool IATSearch::isIATPointerValid(DWORD_PTR iatPointer, bool checkRedirects) { DWORD_PTR apiAddress = 0; if (!readMemoryFromProcess(iatPointer,sizeof(DWORD_PTR),&apiAddress)) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"isIATPointerValid :: error reading memory"); #endif return false; } //printf("Win api ? %08X\n",apiAddress); if (isApiAddressValid(apiAddress) != 0) { return true; } else { if (checkRedirects) { //maybe redirected import? //if the address is 2 times inside a memory region it is possible a redirected api if (apiAddress > memoryAddress && apiAddress < (memoryAddress+memorySize)) { return true; } else { getMemoryRegionFromAddress(apiAddress, &memoryAddress, &memorySize); } } } return false; }
bool IATSearch::findIATStartAndSize(DWORD_PTR address, DWORD_PTR * addressIAT, DWORD * sizeIAT) { BYTE *dataBuffer = 0; DWORD_PTR baseAddress = 0; DWORD baseSize = 0; getMemoryBaseAndSizeForIat(address, &baseAddress, &baseSize); if (!baseAddress) return false; dataBuffer = new BYTE[baseSize * (sizeof(DWORD_PTR)*3)]; if (!dataBuffer) return false; ZeroMemory(dataBuffer, baseSize * (sizeof(DWORD_PTR)*3)); if (!readMemoryFromProcess(baseAddress, baseSize, dataBuffer)) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"findIATStartAddress :: error reading memory"); #endif delete [] dataBuffer; return false; } //printf("address %X memBasic.BaseAddress %X memBasic.RegionSize %X\n",address,memBasic.BaseAddress,memBasic.RegionSize); *addressIAT = findIATStartAddress(baseAddress, address, dataBuffer); *sizeIAT = findIATSize(baseAddress, *addressIAT, dataBuffer, baseSize); delete [] dataBuffer; return true; }
// Entry point of the plugin // This function will be called PINdemonium void runPlugin(static HANDLE hProcess, PUNRESOLVED_IMPORT unresolvedImport, unsigned int eip){ //local variable int max_instruction_size = sizeof(UINT8)*15; int insDelta; char buffer[2048]; int j,instruction_size; INSTRUCTION inst; DWORD_PTR invalidApiAddress = 0; MEMORY_BASIC_INFORMATION memBasic = {0}; LPVOID instruction_buffer = (LPVOID)malloc(max_instruction_size); while (unresolvedImport->ImportTableAddressPointer != 0) //last element is a nulled struct { bool resolved = false; insDelta = 0; invalidApiAddress = unresolvedImport->InvalidApiAddress; //get the starting IAT address to be analyzed yet for (j = 0; j < 1000; j++) { //if we cannot query the invalidApiAddress then bypass the analysis of this address SIZE_T result = VirtualQueryEx(hProcess,(LPVOID)invalidApiAddress, &memBasic, sizeof(MEMORY_BASIC_INFORMATION)); if (!result || memBasic.State != MEM_COMMIT || memBasic.Protect == PAGE_NOACCESS) { //if the memory region pointed by invalidApiAddress isn't mapped break the for loop and check the next unresolved import break; } //read the memory pointed by invalidApiAddress of the external process in order to disassembke the first instruction found //we read 15 bytes because in the x86varchitectures the instructions are guaranteed to fit in 15 bytes readMemoryFromProcess(hProcess, invalidApiAddress, max_instruction_size, instruction_buffer); //disassemble the first instruction in the buffer //instruction_size will contains the length of the disassembled instruction (0 if fails) instruction_size = get_instruction(&inst, (BYTE *)instruction_buffer, MODE_32); //if libdasm fails to recognize the insruction bypass this instruction if(instruction_size == 0){ invalidApiAddress = invalidApiAddress + 1; insDelta = insDelta + 1; continue; } get_instruction_string(&inst, FORMAT_ATT, 0, buffer, sizeof(buffer)); //check if it is a jump if (strstr(buffer, "jmp")) { //calculate the correct answer (add the invalidApiAddress to the destination of the jmp because it is a short jump) unsigned int correct_address = ( (unsigned int)std::strtoul(strstr(buffer, "jmp") + 4 + 2, NULL, 16)) + invalidApiAddress; /* printf("\n\n---------------- MINI REP --------------\n"); printf("INST %s: \n", buffer); printf("INVALID API : %08x \n", invalidApiAddress); printf("INST DELTA %d \n", insDelta); printf("IAT POINTER : %p\n", unresolvedImport->ImportTableAddressPointer); printf("CORRECT ADDR : %08x\n", correct_address); //printf("SIZE OF CORRECT ADDR: %d\n", sizeof(correct_address)); printf("---------------- END MINI REP --------------\n\n"); */ //if the target address is in a memory space dedicated to dlls we have finished our check if(correct_address >= 0x50000000 && correct_address <= 0x7f000000){ //subtract the stolen API executed correct_address = correct_address - insDelta; writeMemoryToProcess(hProcess, (DWORD_PTR)(unresolvedImport->ImportTableAddressPointer), sizeof(correct_address), &correct_address); //unresolved import probably resolved resolved = true; break; } //follow the target address of the jmp and continue the search //we don't have to increase the INSDelta beccause the jmp itself is not a stolen API (instruction belonging to the dll) else{ invalidApiAddress = correct_address; continue; } } //if not increment the delta for the next fix (es : if we have encountered 4 instruction before the correct jmp we have to decrement the correct_address by 16 byte) insDelta = insDelta + instruction_size; //check the next row inthe IAT invalidApiAddress = invalidApiAddress + instruction_size; } /* //if we cannot resolve the import fix it with a dummy address so scylla isn't able to resolve the API and it will remove the unresolved import // this functionality is optional (set the flag nullify_unknown_iat_entry_flag as true with command line) because it can break the program if(!resolved){ unsigned int correct_address = 0x0; writeMemoryToProcess(hProcess, (DWORD_PTR)(unresolvedImport->ImportTableAddressPointer), sizeof(correct_address), &correct_address); resolved = false; } */ unresolvedImport++; //next pointer to struct } }
bool ProcessAccessHelp::readMemoryPartlyFromProcess(DWORD_PTR address, SIZE_T size, LPVOID dataBuffer) { DWORD_PTR addressPart = 0; DWORD_PTR readBytes = 0; DWORD_PTR bytesToRead = 0; MEMORY_BASIC_INFORMATION memBasic = {0}; bool returnValue = false; if (!hProcess) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"readMemoryPartlyFromProcess :: hProcess == NULL"); #endif return returnValue; } if (!readMemoryFromProcess(address, size, dataBuffer)) { addressPart = address; do { if (!VirtualQueryEx(ProcessAccessHelp::hProcess,(LPCVOID)addressPart,&memBasic,sizeof(memBasic))) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"readMemoryPartlyFromProcess :: Error VirtualQueryEx %X %X err: %u", addressPart,size, GetLastError()); #endif break; } bytesToRead = memBasic.RegionSize; if ( (readBytes+bytesToRead) > size) { bytesToRead = size - readBytes; } if (memBasic.State == MEM_COMMIT) { if (!readMemoryFromProcess(addressPart, bytesToRead, (LPVOID)((DWORD_PTR)dataBuffer + readBytes))) { break; } } else { ZeroMemory((LPVOID)((DWORD_PTR)dataBuffer + readBytes),bytesToRead); } readBytes += bytesToRead; addressPart += memBasic.RegionSize; } while (readBytes < size); if (readBytes == size) { returnValue = true; } } else { returnValue = true; } return returnValue; }