From 99f0599bb113d63588ad4db2d242e57b474c5d61 Mon Sep 17 00:00:00 2001 From: mykola2312 <49044616+mykola2312@users.noreply.github.com> Date: Mon, 20 Apr 2020 01:16:17 +0300 Subject: [PATCH] Initial commit --- ps.c | 492 ++++++++++++++++++++++++++++++++++++++++++ ps.h | 102 +++++++++ ps_image.c | 201 +++++++++++++++++ ps_image.h | 58 +++++ ps_inject.c | 144 +++++++++++++ ps_inject.h | 30 +++ ps_target.h | 16 ++ pslib.vcxproj | 134 ++++++++++++ pslib.vcxproj.filters | 42 ++++ 9 files changed, 1219 insertions(+) create mode 100644 ps.c create mode 100644 ps.h create mode 100644 ps_image.c create mode 100644 ps_image.h create mode 100644 ps_inject.c create mode 100644 ps_inject.h create mode 100644 ps_target.h create mode 100644 pslib.vcxproj create mode 100644 pslib.vcxproj.filters diff --git a/ps.c b/ps.c new file mode 100644 index 0000000..71c3175 --- /dev/null +++ b/ps.c @@ -0,0 +1,492 @@ +#include "ps.h" +#include +#include +#include + +static process_t _ps_this_process; + +process_t* ps_this_process = &_ps_this_process; +pe_t* ps_this_module = NULL; + +DWORD ps_page_size; +DWORD ps_page_mask; +DWORD ps_page_power; + +static HMODULE _hMods[1024]; + +static LPCSTR s_szGlobalModules[] = { + "ntdll", + "kernel32" +}; + +static BOOL _ps_is_global_module(LPCSTR lpModuleName) +{ + CHAR szModuleName[64]; + strcpy_s(szModuleName, sizeof(szModuleName), lpModuleName); + _strlwr_s(szModuleName, sizeof(szModuleName)); + + SIZE_T uNameLen = strlen(lpModuleName); + for (DWORD i = 0; i < sizeof(s_szGlobalModules) / sizeof(LPCSTR); i++) + { + LPCSTR lpCurrentModule = s_szGlobalModules[i]; + SIZE_T uCurrentNameLen = strlen(lpCurrentModule); + if (!memcmp(lpCurrentModule, szModuleName, + min(min(uNameLen,uCurrentNameLen), sizeof(szModuleName)))) + { + return TRUE; + } + } + return FALSE; +} + +static DWORD _log2(DWORD dwNum) +{ + DWORD dwPower = 0; + while (dwNum >>= 1) dwPower++; + return dwPower; +} + +void ps_init() +{ + DWORD cbNeeded = 0; + SYSTEM_INFO sysInfo; + + GetSystemInfo(&sysInfo); + ps_page_size = sysInfo.dwPageSize; + ps_page_mask = ps_page_size - 1; + ps_page_power = _log2(ps_page_size); + + ps_this_process->hProcess = GetCurrentProcess(); + + EnumProcessModules(ps_this_process->hProcess, _hMods, sizeof(_hMods), &cbNeeded); + ps_this_process->dwModules = cbNeeded / sizeof(HMODULE); + ps_this_process->pModules = (pe_t*)calloc(ps_this_process->dwModules, sizeof(pe_t)); + + CHAR szExePath[MAX_PATH]; + + GetProcessImageFileName(ps_this_process->hProcess, szExePath, MAX_PATH); + PSTR pExeName = strrchr(szExePath, '\\') + 1; + + CHAR szModulePath[MAX_PATH]; + for (DWORD i = 0; i < ps_this_process->dwModules; i++) + { + pe_t* mod = &ps_this_process->pModules[i]; + + mod->BaseAddress = (ULONG_PTR)_hMods[i]; + mod->pDos = (PIMAGE_DOS_HEADER)mod->BaseAddress; + mod->pNt = (PIMAGE_NT_HEADERS)((PCHAR)mod->pDos + mod->pDos->e_lfanew); + + GetModuleFileName(_hMods[i], szModulePath, MAX_PATH); + PSTR pModuleName = strrchr(szModulePath, '\\') + 1; + strcpy_s(mod->szName, sizeof(mod->szName), pModuleName); + if (!strcmp(pModuleName, pExeName)) + ps_this_process->pExe = mod; + } + + //Find current module + ULONG_PTR ThisFunction = (ULONG_PTR)&ps_init; + for (DWORD i = 0; i < ps_this_process->dwModules; i++) + { + pe_t* mod = &ps_this_process->pModules[i]; + ULONG_PTR CodeStart = mod->BaseAddress + + mod->pNt->OptionalHeader.BaseOfCode; + ULONG_PTR CodeEnd = CodeStart + + mod->pNt->OptionalHeader.SizeOfCode; + if (ThisFunction >= CodeStart && ThisFunction < CodeEnd) + { + ps_this_module = mod; + break; + } + } + + if (!ps_this_module) + { + //Module not found. + //Maybe we're not a DLL but a custom code loader/injector? + //Anyway, redirect to EXE module. + ps_this_module = ps_this_process->pExe; + } +} + +void ps_exit() +{ + free(ps_this_process->pModules); +} + +DWORD ps_round_to_page(DWORD dwSize) +{ + return ((dwSize >> ps_page_power) + (dwSize & ps_page_mask ? 1 : 0)) << ps_page_power; +} + +process_t* ps_load_process(DWORD dwPid) +{ + HANDLE hProcess; + DWORD cbNeeded = 0; + + hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid); + if (hProcess == INVALID_HANDLE_VALUE) return NULL; + process_t* ps = (process_t*)malloc(sizeof(process_t)); + ps->hProcess = hProcess; + + EnumProcessModules(ps->hProcess, _hMods, sizeof(_hMods), &cbNeeded); + DWORD dwModules = cbNeeded / sizeof(HMODULE); + ps->pModules = (pe_t*)calloc(dwModules, sizeof(pe_t)); + CHAR szExePath[MAX_PATH]; + + GetProcessImageFileName(ps->hProcess, szExePath, MAX_PATH); + PSTR pExeName = strrchr(szExePath, '\\') + 1; + + CHAR szModulePath[MAX_PATH]; + + IMAGE_DOS_HEADER dosHeader; + IMAGE_NT_HEADERS ntHeaders; + + ps->dwModules = 0; + for (DWORD i = 0; i < dwModules; i++) + { + pe_t* mod = &ps->pModules[ps->dwModules]; + + mod->BaseAddress = (ULONG_PTR)_hMods[i]; + ps_read(ps, mod->BaseAddress, &dosHeader, sizeof(dosHeader)); + if (dosHeader.e_magic != IMAGE_DOS_SIGNATURE) continue; + ps_read(ps, mod->BaseAddress + dosHeader.e_lfanew, &ntHeaders, sizeof(ntHeaders)); + if (ntHeaders.Signature != IMAGE_NT_SIGNATURE) continue; + + SIZE_T uHdrSize = ntHeaders.OptionalHeader.SizeOfHeaders; + PVOID pHeader = malloc(uHdrSize); + ps_read(ps, mod->BaseAddress, pHeader, uHdrSize); + + mod->pDos = (PIMAGE_DOS_HEADER)pHeader; + mod->pNt = (PIMAGE_NT_HEADERS)((PCHAR)mod->pDos + mod->pDos->e_lfanew); + + GetModuleFileNameEx(ps->hProcess, _hMods[i], szModulePath, MAX_PATH); + PSTR pModuleName = strrchr(szModulePath, '\\') + 1; + strcpy_s(mod->szName, sizeof(mod->szName), pModuleName); + if (!strcmp(pModuleName, pExeName)) + ps->pExe = mod; + + ps->dwModules++; + } + + return ps; +} + +void ps_unload_process(process_t* ps) +{ + if (ps == ps_this_process) return; + + for (DWORD i = 0; i < ps->dwModules; i++) + free(ps->pModules[i].pDos); + free(ps->pModules); + CloseHandle(ps->hProcess); + free(ps); +} + +SIZE_T ps_read(process_t* ps, ULONG_PTR Addr, LPVOID lpDst, SIZE_T Size) +{ + SIZE_T Read = 0; + if (ps == ps_this_process) + { + Read = Size; + CopyMemory(lpDst, (LPCVOID)Addr, Size); + } + else ReadProcessMemory(ps->hProcess, (LPCVOID)Addr, lpDst, Size, &Read); + return Read; +} + +SIZE_T ps_write(process_t* ps, ULONG_PTR Addr, LPCVOID lpSrc, SIZE_T Size) +{ + SIZE_T Write; + if (ps == ps_this_process) + { + Write = Size; + CopyMemory((LPVOID)Addr, lpSrc, Size); + } + else + { + if (!WriteProcessMemory(ps->hProcess, (LPVOID)Addr, lpSrc, Size, &Write)) + return 0; + } + return Write; +} + +pe_t* ps_get_module(process_t* ps, LPCSTR lpModuleName) +{ + CHAR szModuleName[64]; + strcpy_s(szModuleName, sizeof(szModuleName), lpModuleName); + _strlwr_s(szModuleName,sizeof(szModuleName)); + + if (_ps_is_global_module(lpModuleName) && ps != ps_this_process) + return ps_get_module(ps_this_process, lpModuleName); + + SIZE_T uNameLen = strlen(szModuleName); + for (DWORD i = 0; i < ps->dwModules; i++) + { + CHAR szCurrentModuleName[64]; + pe_t* pe = &ps->pModules[i]; + strcpy_s(szCurrentModuleName, sizeof(szCurrentModuleName), pe->szName); + _strlwr_s(szCurrentModuleName,sizeof(szCurrentModuleName)); + SIZE_T uCurrentNameLen = strlen(szModuleName); + if (!memcmp(szCurrentModuleName, szModuleName, + min(min(uNameLen,uCurrentNameLen), sizeof(szModuleName)))) + { + return pe; + } + } + return NULL; +} + +static ULONG_PTR _ps_sig_scan(PCSTR pCode, DWORD dwCodeSize, LPCSTR lpSig, LPCSTR lpMask) +{ + DWORD dwSigLen = (DWORD)strlen(lpMask); + for (DWORD i = 0; i < dwCodeSize - dwSigLen; i++) + { + DWORD j = 0; + for (; j < dwSigLen; j++) + { + if (pCode[i + j] != lpSig[j] && lpMask[j] != '?') + break; + } + if (j == dwSigLen) return i; + } + return 0; +} + +ULONG_PTR ps_sig_scan(process_t* ps, pe_t* mod, LPCSTR lpSig, LPCSTR lpMask) +{ + ULONG_PTR CodeVA = mod->BaseAddress + mod->pNt->OptionalHeader.BaseOfCode; + DWORD dwCodeSize = mod->pNt->OptionalHeader.SizeOfCode; + PCHAR pCode = (PCHAR)malloc(dwCodeSize); + + ps_read(ps, CodeVA, pCode, dwCodeSize); + ULONG_PTR Offset = _ps_sig_scan(pCode, dwCodeSize, lpSig, lpMask); + + free(pCode); + return Offset ? CodeVA + Offset : 0; +} + +ps_seg_t* ps_load_segment(process_t* ps, pe_t* mod, ULONG_PTR Address, DWORD dwSize) +{ + LPVOID lpData; + + BOOL bIsGlobal = _ps_is_global_module(mod->szName); + if (ps == ps_this_process || bIsGlobal) + { + //Directly "project" segment + lpData = (LPVOID)Address; + if (bIsGlobal) ps = ps_this_process; + } + else + { + lpData = malloc(dwSize); + if (!lpData) return NULL; + if (ps_read(ps, Address, lpData, dwSize) == 0) + return NULL; + } + ps_seg_t* seg = (ps_seg_t*)malloc(sizeof(ps_seg_t)); + seg->pProcess = ps; + seg->pMod = mod; + seg->pSeg = (PCHAR)lpData; + seg->dwSegSize = dwSize; + seg->dwSegRVA = (DWORD)(Address - mod->BaseAddress); + + return seg; +} + +ps_seg_t* ps_load_directory(process_t* ps, pe_t* mod, UINT Dir) +{ + UINT_PTR Addr = mod->BaseAddress + mod->pNt->OptionalHeader + .DataDirectory[Dir].VirtualAddress; + DWORD dwSize = mod->pNt->OptionalHeader.DataDirectory[Dir].Size; + return ps_load_segment(ps, mod, Addr, dwSize); +} + +void ps_unload_segment(ps_seg_t* seg) +{ + if (seg->pProcess != ps_this_process) + free(seg->pSeg); + free(seg); +} + +PIMAGE_SECTION_HEADER ps_get_section_header(pe_t* mod, LPCSTR lpSectionName) +{ + PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER) + ((PCHAR)mod->pNt + sizeof(IMAGE_NT_HEADERS)); + for (int i = 0; i < mod->pNt->FileHeader.NumberOfSections; i++) + { + SIZE_T uNameLen = strlen(lpSectionName); + if (!memcmp(pSection->Name, lpSectionName, min(uNameLen, 8))) + return pSection; + pSection++; + } + return NULL; +} + +LPVOID ps_find_section(ps_seg_t* seg, LPCSTR lpSectionName) +{ + PIMAGE_SECTION_HEADER pSection = ps_get_section_header(seg->pMod, lpSectionName); + if (!pSection) return NULL; + return ps_segment_addr(seg, pSection->VirtualAddress); +} + +DWORD ps_section_size(PIMAGE_SECTION_HEADER pSection) +{ + DWORD dwSize = pSection->PointerToRawData + ? pSection->SizeOfRawData : pSection->Misc.VirtualSize; + return ps_round_to_page(dwSize); +} + +BOOL ps_is_in_directory(pe_t* pe, UINT Dir, ULONG_PTR Addr) +{ + PIMAGE_DATA_DIRECTORY pDir = &pe->pNt->OptionalHeader.DataDirectory[Dir]; + ULONG_PTR Start = pe->BaseAddress + pDir->VirtualAddress; + ULONG_PTR End = Start + pDir->Size; + return (Addr >= Start) && (Addr < End); +} + +ULONG_PTR export_get_function(pe_export_t* exp, WORD wOrdinal) +{ + PIMAGE_DATA_DIRECTORY pExportDir = &exp->pMod->pNt->OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; + PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY) + ps_directory(exp, IMAGE_DIRECTORY_ENTRY_EXPORT); + PDWORD pFunctionRVA = (PDWORD)ps_segment_addr(exp, pExport->AddressOfFunctions); + DWORD dwExportEntry = pFunctionRVA[wOrdinal - pExport->Base]; + ULONG_PTR Addr = exp->pMod->BaseAddress + dwExportEntry; + if (ps_is_in_directory(exp->pMod, IMAGE_DIRECTORY_ENTRY_EXPORT, Addr)) + { + //Forwarder RVA + CHAR szName[64]; + + strcpy_s(szName, sizeof(szName), (LPCSTR)ps_segment_addr(exp,dwExportEntry)); + LPCSTR lpModuleName = szName; + PCHAR pSep = strrchr(szName, '.'); + if (!pSep) return 0; + *pSep = 0; + LPCSTR lpFuncName = ++pSep; + + pe_t* fwdMod = ps_get_module(exp->pProcess, lpModuleName); + if (!fwdMod) return 0; + pe_export_t* fwdExp = export_load(exp->pProcess, fwdMod); + //Forward by ordinal or name + Addr = export_find_function(fwdExp, lpFuncName); + export_unload(fwdExp); + } + return Addr; +} + +ULONG_PTR export_find_function(pe_export_t* exp, LPCSTR lpName) +{ + if (lpName[0] == '#') + return export_get_function(exp, (WORD)atoi(lpName + 1)); + + PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY) + ps_directory(exp, IMAGE_DIRECTORY_ENTRY_EXPORT); + PDWORD ppNameRVA = (PDWORD)ps_segment_addr(exp, pExport->AddressOfNames); + PWORD pOrdinals = (PWORD)ps_segment_addr(exp, pExport->AddressOfNameOrdinals); + for (DWORD i = 0; i < pExport->NumberOfNames; i++) + { + LPSTR lpFuncName = (LPSTR)ps_segment_addr(exp, ppNameRVA[i]); + if (!strcmp(lpFuncName, lpName)) + return export_get_function(exp, (WORD)pExport->Base + pOrdinals[i]); + } + return 0; +} + +static pe_reloc_t* reloc_init(ps_seg_t* dataSeg) +{ + if (dataSeg->pMod->pNt->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED) + return NULL; + PVOID lpReloc = ps_directory(dataSeg, IMAGE_DIRECTORY_ENTRY_BASERELOC); + + pe_reloc_t* rel = (pe_reloc_t*)malloc(sizeof(pe_reloc_t)); + rel->pData = dataSeg; + rel->pBlock = (PIMAGE_BASE_RELOCATION)lpReloc; + rel->dwCurrentReloc = 0; + + return rel; +} + +pe_reloc_t* reloc_load(process_t* ps, pe_t* mod) +{ + ps_seg_t* dataSeg = ps_load_directory(ps, mod, IMAGE_DIRECTORY_ENTRY_BASERELOC); + if (!dataSeg) return NULL; + return reloc_init(dataSeg); +} + +#define _relocs_count(size) ((size - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD)) + +WORD reloc_next(pe_reloc_t* rel) +{ + if (rel->dwCurrentReloc == _relocs_count(rel->pBlock->SizeOfBlock)) + { + rel->pBlock = (PIMAGE_BASE_RELOCATION)((PCHAR)rel->pBlock + rel->pBlock->SizeOfBlock); + rel->dwCurrentReloc = 0; + } + + if (rel->pBlock == (PIMAGE_BASE_RELOCATION)ps_directory_end( + rel->pData, IMAGE_DIRECTORY_ENTRY_BASERELOC)) + return PS_NO_RELOC; + + return ((PWORD)((PCHAR)rel->pBlock + sizeof(IMAGE_BASE_RELOCATION)))[rel->dwCurrentReloc++]; +} + +void reloc_unload(pe_reloc_t* rel) +{ + ps_unload_segment(rel->pData); + free(rel); +} + +pe_import_t* import_load(process_t* ps, pe_t* mod) +{ + pe_import_t* imp = (pe_import_t*)malloc(sizeof(pe_import_t)); + imp->pImport = ps_load_directory(ps, mod, IMAGE_DIRECTORY_ENTRY_IMPORT); + imp->pIAT = ps_load_directory(ps, mod, IMAGE_DIRECTORY_ENTRY_IAT); + return imp; +} + +BOOL import_find_thunk(pe_import_t* imp, ULONG_PTR ThunkAddr, PCHAR pszLibName, SIZE_T uLibNameLen, + PCHAR pszFuncName, SIZE_T uFuncNameLen) +{ + PIMAGE_IMPORT_DESCRIPTOR pDesc = ps_directory(imp->pImport, IMAGE_DIRECTORY_ENTRY_IMPORT); + while (pDesc->Characteristics) + { + PIMAGE_THUNK_DATA pOriginalThunk = (PIMAGE_THUNK_DATA) + ps_segment_addr(imp->pImport, pDesc->OriginalFirstThunk); + PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA) + ps_segment_addr(imp->pIAT, pDesc->FirstThunk); + while (pOriginalThunk->u1.Function) + { + if ((ULONG_PTR)pThunk == ThunkAddr) + { + strcpy_s(pszLibName, uLibNameLen, (LPCSTR) + ps_segment_addr(imp->pImport, pDesc->Name)); + if (IMAGE_SNAP_BY_ORDINAL(pOriginalThunk->u1.Ordinal)) + { + sprintf_s(pszFuncName, uFuncNameLen, "#%u", + IMAGE_ORDINAL(pOriginalThunk->u1.Ordinal)); + } + else + { + PIMAGE_IMPORT_BY_NAME pName = (PIMAGE_IMPORT_BY_NAME) + ps_segment_addr(imp->pImport, pOriginalThunk->u1.AddressOfData); + strcpy_s(pszFuncName, uFuncNameLen, pName->Name); + } + return TRUE; + } + + pOriginalThunk++; + pThunk++; + } + + pDesc++; + } + return FALSE; +} + +void import_unload(pe_import_t* imp) +{ + ps_unload_segment(imp->pImport); + ps_unload_segment(imp->pIAT); + free(imp); +} \ No newline at end of file diff --git a/ps.h b/ps.h new file mode 100644 index 0000000..cbe1f4a --- /dev/null +++ b/ps.h @@ -0,0 +1,102 @@ +#ifndef __PS_H +#define __PS_H + +#include + +typedef struct { + ULONG_PTR BaseAddress; + PIMAGE_DOS_HEADER pDos; + PIMAGE_NT_HEADERS pNt; + CHAR szName[64]; +} pe_t; + +typedef struct { + HANDLE hProcess; + DWORD dwModules; + pe_t* pModules; + pe_t* pExe; +} process_t; + +//For data or code. Segment of memory +typedef struct { + process_t* pProcess; + pe_t* pMod; + //Segment info + PCHAR pSeg; + DWORD dwSegSize; + DWORD dwSegRVA; +} ps_seg_t; + +typedef ps_seg_t pe_export_t; + +extern process_t* ps_this_process; +extern pe_t* ps_this_module; + +extern DWORD ps_page_size; +extern DWORD ps_page_mask; +extern DWORD ps_page_power; + +void ps_init(); +void ps_exit(); + +DWORD ps_round_to_page(DWORD dwSize); + +process_t* ps_load_process(DWORD dwPid); +void ps_unload_process(process_t* ps); + +SIZE_T ps_read(process_t* ps, ULONG_PTR Addr, LPVOID lpDst, SIZE_T Size); +SIZE_T ps_write(process_t* ps, ULONG_PTR Addr, LPCVOID lpSrc, SIZE_T Size); + +pe_t* ps_get_module(process_t* ps, LPCSTR lpModuleName); +ULONG_PTR ps_sig_scan(process_t* ps, pe_t* mod, LPCSTR lpSig, LPCSTR lpMask); + +ps_seg_t* ps_load_segment(process_t* ps, pe_t* mod, ULONG_PTR Address, DWORD dwSize); +ps_seg_t* ps_load_directory(process_t* ps, pe_t* mod, UINT Dir); +void ps_unload_segment(ps_seg_t* seg); +#define ps_segment_addr(seg,rva) (PVOID)((PCHAR)seg->pSeg + rva - seg->dwSegRVA) +#define ps_directory(seg,dir) ps_segment_addr(seg,seg->pMod->pNt->OptionalHeader.DataDirectory[dir].VirtualAddress) +#define ps_directory_size(seg,dir) seg->pMod->pNt->OptionalHeader.DataDirectory[dir].Size +#define ps_directory_end(seg,dir) (PVOID)((PCHAR)ps_directory(seg,dir) + ps_directory_size(seg,dir)) + +PIMAGE_SECTION_HEADER ps_get_section_header(pe_t* mod, LPCSTR lpSectionName); +LPVOID ps_find_section(ps_seg_t* seg, LPCSTR lpSectionName); +DWORD ps_section_size(PIMAGE_SECTION_HEADER pSection); +BOOL ps_is_in_directory(pe_t* pe, UINT Dir, ULONG_PTR Addr); + +#define ps_load_code(ps,mod) ps_load_segment_class(ps,mod,PS_SEG_CODE) +#define ps_unload_code ps_unload_segment +#define ps_load_data(ps,mod) ps_load_segment_class(ps,mod,PS_SEG_DATA) +#define ps_unload_data ps_unload_segment + +#define ps_load_directory_segment(ps,mod,dir) \ + ps_load_segment_class(ps,mod,ps_determine_directory_segment(mod,dir)) \ + +//#define export_load(ps,mod) ps_load_directory_segment(ps,mod,IMAGE_DIRECTORY_ENTRY_EXPORT) +#define export_load(ps,mod) ps_load_directory(ps,mod,IMAGE_DIRECTORY_ENTRY_EXPORT) +ULONG_PTR export_get_function(pe_export_t* exp, WORD wOrdinal); +ULONG_PTR export_find_function(pe_export_t* exp, LPCSTR lpName); +#define export_unload ps_unload_segment + +typedef struct { + ps_seg_t* pData; + PIMAGE_BASE_RELOCATION pBlock; + DWORD dwCurrentReloc; +} pe_reloc_t; + +#define PS_NO_RELOC (WORD)-1 + +pe_reloc_t* reloc_load(process_t* ps, pe_t* mod); +WORD reloc_next(pe_reloc_t* rel); +void reloc_unload(pe_reloc_t* rel); + +typedef struct { + ps_seg_t* pImport; + ps_seg_t* pIAT; +} pe_import_t; + +pe_import_t* import_load(process_t* ps, pe_t* mod); +BOOL import_find_thunk(pe_import_t* imp, ULONG_PTR ThunkAddr, PCHAR pszLibName, SIZE_T uLibNameLen, + PCHAR pszFuncName,SIZE_T uFuncNameLen); +void import_unload(pe_import_t* imp); + +#endif \ No newline at end of file diff --git a/ps_image.c b/ps_image.c new file mode 100644 index 0000000..a2dc6f8 --- /dev/null +++ b/ps_image.c @@ -0,0 +1,201 @@ +#include "ps_image.h" + +static DWORD _ps_section_flags[PS_IMAGE_SECTIONS] = { + PAGE_EXECUTE_READ, + PAGE_READWRITE, + PAGE_READONLY +}; + +static DWORD _ps_image_vsec_init_size = PS_DEFAULT_RDATA_RSIZE; + +static BOOL _ps_in_section(ULONG_PTR Base, PIMAGE_SECTION_HEADER pSection, ULONG_PTR Addr) +{ + ULONG_PTR Start = Base + pSection->VirtualAddress; + ULONG_PTR End = Start + ps_section_size(pSection); + return (Addr >= Start) && (Addr < End); +} + +static INT _ps_determine_section(ULONG_PTR Base,PIMAGE_SECTION_HEADER* pHeaders, DWORD dwNum, ULONG_PTR Addr) +{ + for (DWORD i = 0; i < dwNum; i++) + { + ULONG_PTR Start = Base + pHeaders[i]->VirtualAddress; + ULONG_PTR End = Start + ps_round_to_page(pHeaders[i]->SizeOfRawData); + if (Addr >= Start && Addr < End) + return (INT)i; + } + return -1; +} + +ps_image_t* ps_image_create(pe_t* mod, ULONG_PTR VirtualBase, + LPCSTR lpCodeSectionName, LPCSTR lpDataSectionName) +{ + PIMAGE_SECTION_HEADER pHeaders[PS_IMAGE_SECTIONS]; + + pHeaders[PS_IMAGE_SECTION_CODE] = ps_get_section_header(mod, lpCodeSectionName); + pHeaders[PS_IMAGE_SECTION_DATA] = ps_get_section_header(mod, lpDataSectionName); + pHeaders[PS_IMAGE_SECTION_RDATA] = NULL; + if (!pHeaders[PS_IMAGE_SECTION_CODE] || !pHeaders[PS_IMAGE_SECTION_DATA]) + return NULL; + + ps_image_t* image = (ps_image_t*)malloc(sizeof(ps_image_t)); + image->VirtualBase = VirtualBase; + image->dwImports = 0; + image->import = NULL; + + //Initialize sections + image->dwVirtualSize = 0; + for (DWORD i = 0; i < PS_IMAGE_SECTIONS; i++) + { + image->RSec[i].dwRealSize = pHeaders[i] + ? pHeaders[i]->SizeOfRawData : _ps_image_vsec_init_size; + image->RSec[i].pData = (PCHAR)malloc(image->RSec[i].dwRealSize); + + image->VSec[i].dwRVA = image->dwVirtualSize; + image->VSec[i].dwSize = ps_round_to_page(image->RSec[i].dwRealSize); + image->VSec[i].dwFlags = _ps_section_flags[i]; + + if (pHeaders[i]) + { + LPCVOID lpSource = (LPCVOID)(mod->BaseAddress + pHeaders[i]->VirtualAddress); + CopyMemory(image->RSec[i].pData, lpSource, image->RSec[i].dwRealSize); + } + else ZeroMemory(image->RSec[i].pData, image->RSec[i].dwRealSize); + + image->dwVirtualSize += image->VSec[i].dwSize; + } + + ULONG_PTR ModBase = mod->BaseAddress; + +#ifndef _WIN64 + pe_reloc_t* rel = reloc_load(ps_this_process, mod); + WORD wReloc; + + pe_import_t* imp = import_load(ps_this_process, mod); + CHAR szLibName[64]; + CHAR szFuncName[64]; + + while ((wReloc = reloc_next(rel)) != PS_NO_RELOC) + { + WORD wType = wReloc >> 12; + ULONG_PTR RelocAddr = ModBase + (rel->pBlock->VirtualAddress | (wReloc & 0xFFF)); + if (wType != IMAGE_REL_BASED_HIGHLOW && wType != IMAGE_REL_BASED_DIR64) + continue; + INT iSrcSec = _ps_determine_section(ModBase, pHeaders, 2, RelocAddr); + if (iSrcSec == -1) continue; + + PULONG_PTR DestAddr = (PULONG_PTR)(image->RSec[iSrcSec].pData + + ((RelocAddr - pHeaders[iSrcSec]->VirtualAddress) - ModBase)); + + if (import_find_thunk(imp, *(PULONG_PTR)RelocAddr, szLibName, sizeof(szLibName), + szFuncName, sizeof(szFuncName))) + { + ps_import_thunk_t* thunk = ps_image_import_add(image, szLibName, szFuncName); + *DestAddr = image->VirtualBase + image->VSec[PS_IMAGE_SECTION_RDATA].dwRVA + + (thunk->dwIndex * sizeof(ULONG_PTR)); + } + else + { + INT iDstSec = _ps_determine_section(ModBase, pHeaders, 2, *(PULONG_PTR)RelocAddr); + if (iDstSec == -1) continue; + + *DestAddr = image->VirtualBase + image->VSec[iDstSec].dwRVA + + ((*(PULONG_PTR)RelocAddr - ModBase) - pHeaders[iDstSec]->VirtualAddress); + } + } +#endif + + reloc_unload(rel); + import_unload(imp); + + return image; +} + +//ps_image_import_add: Find or create thunk import +ps_import_thunk_t* ps_image_import_add(ps_image_t* image, LPCSTR lpLibName, LPCSTR lpFuncName) +{ + ps_import_lib_t* lib = NULL; + ps_import_lib_t* lastLib = NULL; + + ps_import_thunk_t* thunk = NULL; + ps_import_thunk_t* lastThunk = NULL; + + //Find or create ps_import_lib_t + lastLib = image->import; + while (lastLib) + { + SIZE_T uNameLen = strlen(lpLibName); + if (!memcmp(lastLib->szLibName, lpLibName, + min(uNameLen, sizeof(lastLib->szLibName)))) + { + if (!lib) lib = lastLib; + } + if (!lastLib->next) break; + else lastLib = lastLib->next; + } + + if (!lib) + { + lib = (ps_import_lib_t*)malloc(sizeof(ps_import_lib_t)); + lib->thunk = NULL; + lib->next = NULL; + strcpy_s(lib->szLibName, sizeof(lib->szLibName), lpLibName); + if (lastLib) lastLib->next = lib; + else image->import = lib; + } + + //Find or create ps_import_thunk_t + lastThunk = lib->thunk; + while (lastThunk) + { + SIZE_T uNameLen = strlen(lpFuncName); + if (!memcmp(lastThunk->szFuncName, lpFuncName, + min(uNameLen, sizeof(lastThunk->szFuncName)))) + { + if (!thunk) thunk = lastThunk; + } + if (!lastThunk->next) break; + else lastThunk = lastThunk->next; + } + + if (!thunk) + { + thunk = (ps_import_thunk_t*)malloc(sizeof(ps_import_thunk_t)); + thunk->dwIndex = image->dwImports++; + thunk->next = NULL; + strcpy_s(thunk->szFuncName, sizeof(thunk->szFuncName), lpFuncName); + if (lastThunk) lastThunk->next = thunk; + else lib->thunk = thunk; + } + + return thunk; +} + +static void _ps_image_import_free(ps_import_lib_t* lib) +{ + ps_import_lib_t* libItem = lib; + + while (libItem) + { + ps_import_thunk_t* thunkItem = libItem->thunk; + + while (thunkItem) + { + ps_import_thunk_t* thunkNext = thunkItem->next; + free(thunkItem); + thunkItem = thunkNext; + } + + ps_import_lib_t* libNext = libItem->next; + free(libItem); + libItem = libNext; + } +} + +void ps_image_free(ps_image_t* image) +{ + _ps_image_import_free(image->import); + for (DWORD i = 0; i < PS_IMAGE_SECTIONS; i++) + free(image->RSec[i].pData); + free(image); +} \ No newline at end of file diff --git a/ps_image.h b/ps_image.h new file mode 100644 index 0000000..d5dbd65 --- /dev/null +++ b/ps_image.h @@ -0,0 +1,58 @@ +#ifndef __PS_IMAGE_H +#define __PS_IMAGE_H + +#include "ps.h" + +#define PS_DEFAULT_RDATA_RSIZE 4096 +#define PS_DEFAULT_RDATA_VSIZE 4096 + +typedef struct { + DWORD dwRVA; + DWORD dwSize; + DWORD dwFlags; +} ps_virtual_section_t; + +typedef struct { + PCHAR pData; + DWORD dwRealSize; +} ps_real_section_t; + +typedef enum { + PS_IMAGE_SECTION_CODE = 0, //For all program code + PS_IMAGE_SECTION_DATA, //For all program data + PS_IMAGE_SECTION_RDATA, //For all service data like IAT + PS_IMAGE_SECTIONS +} ps_section_type; + +typedef struct ps_import_thunk_s { + CHAR szFuncName[64]; + DWORD dwIndex; //Virtual IAT + struct ps_import_thunk_s* next; +} ps_import_thunk_t; + +typedef struct ps_import_lib_s { + CHAR szLibName[64]; + ps_import_thunk_t* thunk; + struct ps_import_lib_s* next; +} ps_import_lib_t; + +typedef struct { + //Virtual Image + ULONG_PTR VirtualBase; + DWORD dwVirtualSize; + + ps_real_section_t RSec[PS_IMAGE_SECTIONS]; + ps_virtual_section_t VSec[PS_IMAGE_SECTIONS]; + + DWORD dwImports; //Virtual IAT import count + ps_import_lib_t* import; +} ps_image_t; + +ps_image_t* ps_image_create(pe_t* mod,ULONG_PTR VirtualBase, + LPCSTR lpCodeSectionName,LPCSTR lpDataSectionName); + +ps_import_thunk_t* ps_image_import_add(ps_image_t* image, LPCSTR lpLibName, LPCSTR lpFuncName); + +void ps_image_free(ps_image_t* image); + +#endif \ No newline at end of file diff --git a/ps_inject.c b/ps_inject.c new file mode 100644 index 0000000..896f40d --- /dev/null +++ b/ps_inject.c @@ -0,0 +1,144 @@ +#include "ps_inject.h" +#include "ps_image.h" + +int ps_inject(process_t* ps, pe_t* thisMod, LPCSTR lpCodeSec, LPCSTR lpDataSec, + LPVOID lpEntryPoint, ps_inject_t* inject) +{ + PIMAGE_SECTION_HEADER pCodeHdr = ps_get_section_header(thisMod, lpCodeSec); + PIMAGE_SECTION_HEADER pDataHdr = ps_get_section_header(thisMod, lpDataSec); + if (!pCodeHdr || !pDataHdr) return PS_NO_SECTIONS; + + inject->dwCodeSize = ps_section_size(pCodeHdr); + inject->dwDataSize = ps_section_size(pDataHdr); + inject->dwSize = inject->dwCodeSize + inject->dwDataSize + ps_page_size; //+1 page for PS_IMAGE_RDATA + inject->Base = (ULONG_PTR)VirtualAllocEx(ps->hProcess, NULL, inject->dwSize, MEM_RESERVE, PAGE_READWRITE); + + ps_image_t* image = ps_image_create(thisMod, inject->Base, lpCodeSec, lpDataSec); + if (!image) + { + VirtualFreeEx(ps->hProcess, (LPVOID)inject->Base, inject->dwSize, MEM_RELEASE); + return PS_CREATE_IMAGE_FAILED; + } + + ps_import_lib_t* lib = image->import; + while (lib) + { + ps_import_thunk_t* thunk = lib->thunk; + + pe_t* pLib = ps_get_module(ps, lib->szLibName); + if (!pLib) continue; + pe_export_t* exp = export_load(ps, pLib); + if (!exp) continue; + + while (thunk) + { + ((PULONG_PTR)image->RSec[PS_IMAGE_SECTION_RDATA].pData)[thunk->dwIndex] + = export_find_function(exp, thunk->szFuncName); + thunk = thunk->next; + } + + export_unload(exp); + lib = lib->next; + } + + for (DWORD i = 0; i < PS_IMAGE_SECTIONS; i++) + { + DWORD dwOldProtect; + ULONG_PTR Addr = image->VirtualBase + image->VSec[i].dwRVA; + VirtualAllocEx(ps->hProcess, (LPVOID)Addr, image->VSec[i].dwSize, MEM_COMMIT, PAGE_READWRITE); + ps_write(ps, Addr, image->RSec[i].pData, image->RSec[i].dwRealSize); + VirtualProtectEx(ps->hProcess, (LPVOID)Addr, + image->VSec[i].dwSize, image->VSec[i].dwFlags, &dwOldProtect); + } + + inject->dwSize = image->dwVirtualSize; + inject->CodeVA = image->VirtualBase + image->VSec[PS_IMAGE_SECTION_CODE].dwRVA; + inject->DataVA = image->VirtualBase + image->VSec[PS_IMAGE_SECTION_DATA].dwRVA; + inject->EntryPointVA = inject->CodeVA + ((ULONG_PTR)lpEntryPoint + - (thisMod->BaseAddress + pCodeHdr->VirtualAddress)); + + ps_image_free(image); + return PS_INJECT_OK; +} + +BOOL ps_adjust_privilege(LPCSTR lpPrivilegeName) +{ + HANDLE hToken; + LUID luidPrivilege; + TOKEN_PRIVILEGES privileges; + + if (!LookupPrivilegeValue(NULL, lpPrivilegeName, &luidPrivilege)) + return FALSE; + if (!OpenProcessToken(ps_this_process->hProcess, TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken)) + return FALSE; + + privileges.PrivilegeCount = 1; + privileges.Privileges[0].Luid = luidPrivilege; + privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + AdjustTokenPrivileges(hToken, FALSE, &privileges, sizeof(privileges), NULL, NULL); + CloseHandle(hToken); + + return TRUE; +} + +static BYTE _ps_hijack_boot[16]; + +#define OP_PUSHFD 0x9C +#define OP_PUSHAD 0x60 + +#define OP_POPAD 0x61 +#define OP_POPFD 0x9D + +#define OP_CALL 0xE8 +#define OP_JMP 0xE9 + +#define JMP_OFFSET(from,to) ((to) - (from) - 5) + +BOOL ps_hijack_thread(process_t* ps, DWORD dwTid, ULONG_PTR Code) +{ + CONTEXT Ctx; + DWORD dwOldProtect; + HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION + | THREAD_SUSPEND_RESUME + | THREAD_GET_CONTEXT + | THREAD_SET_CONTEXT, + FALSE, dwTid); + if (hThread == INVALID_HANDLE_VALUE) + return FALSE; + + ZeroMemory(&Ctx, sizeof(CONTEXT)); + Ctx.ContextFlags = CONTEXT_CONTROL; + + SuspendThread(hThread); + GetThreadContext(hThread, &Ctx); + + ULONG_PTR BootCode = (ULONG_PTR)VirtualAllocEx(ps->hProcess, NULL, + 4096, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + ULONG_PTR PrevAddr = Ctx.Eip; + + //Build shell code + _ps_hijack_boot[0] = OP_PUSHFD; + _ps_hijack_boot[1] = OP_PUSHAD; + + _ps_hijack_boot[2] = OP_CALL; + *(PLONG_PTR)&_ps_hijack_boot[3] = JMP_OFFSET((LONG_PTR)BootCode + 2, (LONG_PTR)Code); + + _ps_hijack_boot[7] = OP_POPAD; + _ps_hijack_boot[8] = OP_POPFD; + + _ps_hijack_boot[9] = OP_JMP; + *(PLONG_PTR)&_ps_hijack_boot[10] = JMP_OFFSET((LONG_PTR)BootCode + 2, (LONG_PTR)PrevAddr); + + //Write + ps_write(ps, BootCode, _ps_hijack_boot, sizeof(_ps_hijack_boot)); + VirtualProtect((LPVOID)BootCode, sizeof(_ps_hijack_boot), PAGE_EXECUTE_READWRITE, &dwOldProtect); + + //Execute + Ctx.Eip = BootCode; + + SetThreadContext(hThread, &Ctx); + ResumeThread(hThread); + CloseHandle(hThread); + return TRUE; +} \ No newline at end of file diff --git a/ps_inject.h b/ps_inject.h new file mode 100644 index 0000000..1fe1cdd --- /dev/null +++ b/ps_inject.h @@ -0,0 +1,30 @@ +#ifndef __PS_INJECT_H +#define __PS_INJECT_H + +#include "ps.h" + +#define PS_CREATE_IMAGE_FAILED -2 +#define PS_NO_SECTIONS -1 +#define PS_INJECT_OK 0 + +typedef struct { + //Base of "image" in target process + ULONG_PTR Base; + DWORD dwSize; + + ULONG_PTR CodeVA; + DWORD dwCodeSize; + + ULONG_PTR DataVA; + DWORD dwDataSize; + + ULONG_PTR EntryPointVA; +} ps_inject_t; + +int ps_inject(process_t* ps, pe_t* thisMod, LPCSTR lpCodeSec, LPCSTR lpDataSec, + LPVOID lpEntryPoint,ps_inject_t* inject); + +BOOL ps_adjust_privilege(LPCSTR lpPrivilegeName); +BOOL ps_hijack_thread(process_t* ps, DWORD dwTid, ULONG_PTR Code); + +#endif \ No newline at end of file diff --git a/ps_target.h b/ps_target.h new file mode 100644 index 0000000..94108ec --- /dev/null +++ b/ps_target.h @@ -0,0 +1,16 @@ +#ifndef __PS_TARGET_H +#define __PS_TARGET_H + +#include + +#ifndef _WIN64 + +#define PS_IMPORT(funcName,thunkName) \ + __declspec(naked) ULONG_PTR thunkName() \ + { \ + __asm jmp dword ptr ds : [funcName] \ + } + +#endif + +#endif \ No newline at end of file diff --git a/pslib.vcxproj b/pslib.vcxproj new file mode 100644 index 0000000..85437ec --- /dev/null +++ b/pslib.vcxproj @@ -0,0 +1,134 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {AA1B5896-DAB2-4A98-8978-710A51814757} + pslib + + + + StaticLibrary + true + v120 + MultiByte + + + StaticLibrary + true + v120 + MultiByte + + + StaticLibrary + false + v120 + true + MultiByte + + + StaticLibrary + false + v120 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + true + + + true + + + + + Level3 + Disabled + true + + + true + + + + + Level3 + MinSpace + true + true + true + MultiThreaded + false + false + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + true + + + true + true + true + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pslib.vcxproj.filters b/pslib.vcxproj.filters new file mode 100644 index 0000000..03d6b1f --- /dev/null +++ b/pslib.vcxproj.filters @@ -0,0 +1,42 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file