Full support for ASLR in the Win32 loader.
This commit is contained in:
+1
-3
@@ -69,7 +69,6 @@
|
||||
<ProgramDatabaseFile>$(OutDir)zznop.pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
@@ -91,7 +90,6 @@
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
@@ -106,4 +104,4 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
+74
-157
@@ -73,9 +73,7 @@ static int run_process(struct child *child, struct opts *, int[][2]);
|
||||
|
||||
#if defined HAVE_WINDOWS_H
|
||||
static void rep32(uint8_t *buf, void *addr);
|
||||
static int dll_inject(PROCESS_INFORMATION *, void *, char const *);
|
||||
static intptr_t get_base_address(DWORD);
|
||||
static intptr_t get_entry_point(char const *name, DWORD pid);
|
||||
static int dll_inject(PROCESS_INFORMATION *, char const *);
|
||||
static intptr_t get_proc_address(void *, DWORD, char const *);
|
||||
#endif
|
||||
|
||||
@@ -158,7 +156,6 @@ static int run_process(struct child *child, struct opts *opts, int pipes[][2])
|
||||
PROCESS_INFORMATION pinfo;
|
||||
STARTUPINFO sinfo;
|
||||
HANDLE pid;
|
||||
void *epaddr;
|
||||
int ret;
|
||||
#endif
|
||||
|
||||
@@ -275,14 +272,8 @@ static int run_process(struct child *child, struct opts *opts, int pipes[][2])
|
||||
if(!ret)
|
||||
return -1;
|
||||
|
||||
/* Get the child process's entry point address */
|
||||
epaddr = (void *)get_entry_point(child->newargv[0],
|
||||
pinfo.dwProcessId);
|
||||
if(!epaddr)
|
||||
return -1;
|
||||
|
||||
/* Insert the replacement code */
|
||||
ret = dll_inject(&pinfo, epaddr, SONAME);
|
||||
ret = dll_inject(&pinfo, SONAME);
|
||||
if(ret < 0)
|
||||
{
|
||||
TerminateProcess(pinfo.hProcess, -1);
|
||||
@@ -296,7 +287,6 @@ static int run_process(struct child *child, struct opts *opts, int pipes[][2])
|
||||
return -1;
|
||||
}
|
||||
|
||||
Sleep(5000);
|
||||
return (long int)pinfo.hProcess;
|
||||
#endif
|
||||
}
|
||||
@@ -312,8 +302,7 @@ static void rep32(uint8_t *buf, void *addr)
|
||||
}
|
||||
}
|
||||
|
||||
static int dll_inject(PROCESS_INFORMATION *pinfo,
|
||||
void *epaddr, char const *lib)
|
||||
static int dll_inject(PROCESS_INFORMATION *pinfo, char const *lib)
|
||||
{
|
||||
static uint8_t const loader[] =
|
||||
/* Load the injected DLL into memory */
|
||||
@@ -347,8 +336,10 @@ static int dll_inject(PROCESS_INFORMATION *pinfo,
|
||||
"\xb8____" /* mov eax, <loader_address> */
|
||||
"\xff\xe0"; /* jmp eax */
|
||||
|
||||
CONTEXT ctx;
|
||||
void *process = pinfo->hProcess;
|
||||
void *thread = pinfo->hThread;
|
||||
void *epaddr;
|
||||
DWORD pid = pinfo->dwProcessId;
|
||||
|
||||
/* code:
|
||||
@@ -396,6 +387,11 @@ static int dll_inject(PROCESS_INFORMATION *pinfo,
|
||||
memcpy(code, loader, loaderlen);
|
||||
memcpy(code + loaderlen + jumperlen, lib, liblen);
|
||||
|
||||
/* Find the entry point address. It's simply in EAX. */
|
||||
ctx.ContextFlags = CONTEXT_FULL;
|
||||
GetThreadContext(thread, &ctx);
|
||||
epaddr = (void *)(uintptr_t)ctx.Eax;
|
||||
|
||||
/* Backup the old entry point code */
|
||||
ReadProcessMemory(process, epaddr, code + loaderlen, jumperlen, &tmp);
|
||||
if(tmp != jumperlen)
|
||||
@@ -411,15 +407,15 @@ static int dll_inject(PROCESS_INFORMATION *pinfo,
|
||||
ResumeThread(thread);
|
||||
|
||||
/* Wait until the entry point is reached */
|
||||
for (tmp = 0; tmp < 100; tmp++)
|
||||
{
|
||||
CONTEXT ctx;
|
||||
ctx.ContextFlags = CONTEXT_FULL;
|
||||
GetThreadContext(thread, &ctx);
|
||||
if ((uintptr_t)ctx.Eip == (uintptr_t)epaddr)
|
||||
break;
|
||||
Sleep(10);
|
||||
}
|
||||
for (tmp = 0; tmp < 100; tmp++)
|
||||
{
|
||||
CONTEXT ctx;
|
||||
ctx.ContextFlags = CONTEXT_FULL;
|
||||
GetThreadContext(thread, &ctx);
|
||||
if ((uintptr_t)ctx.Eip == (uintptr_t)epaddr)
|
||||
break;
|
||||
Sleep(10);
|
||||
}
|
||||
SuspendThread(thread);
|
||||
if (tmp == 100)
|
||||
return -1;
|
||||
@@ -428,12 +424,12 @@ static int dll_inject(PROCESS_INFORMATION *pinfo,
|
||||
* of the functions we need. This can only be done because we advanced
|
||||
* the target's execution to the entry point. */
|
||||
rep32(code, loaderaddr + loaderlen + jumperlen);
|
||||
rep32(code, (uintptr_t)get_proc_address(process, pid, "LoadLibraryA"));
|
||||
rep32(code, (void *)get_proc_address(process, pid, "LoadLibraryA"));
|
||||
rep32(code, (void *)(uintptr_t)jumperlen);
|
||||
rep32(code, loaderaddr + loaderlen);
|
||||
rep32(code, epaddr);
|
||||
rep32(code, (uintptr_t)get_proc_address(process, pid, "GetCurrentProcess"));
|
||||
rep32(code, (uintptr_t)get_proc_address(process, pid, "WriteProcessMemory"));
|
||||
rep32(code, (void *)get_proc_address(process, pid, "GetCurrentProcess"));
|
||||
rep32(code, (void *)get_proc_address(process, pid, "WriteProcessMemory"));
|
||||
rep32(code, epaddr);
|
||||
|
||||
/* Write our shellcodes into the target process */
|
||||
@@ -451,148 +447,69 @@ static int dll_inject(PROCESS_INFORMATION *pinfo,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find the process's entry point address offset. The information is in
|
||||
* the file's PE header. */
|
||||
static intptr_t get_entry_point(char const *name, DWORD pid)
|
||||
static intptr_t get_proc_address(void *process, DWORD pid, const char *func)
|
||||
{
|
||||
PIMAGE_DOS_HEADER dos;
|
||||
PIMAGE_NT_HEADERS nt;
|
||||
intptr_t ret = 0;
|
||||
void *file, *map, *base;
|
||||
char buf[1024];
|
||||
size_t buflen = strlen(func) + 1;
|
||||
|
||||
file = CreateFile(name, GENERIC_READ, FILE_SHARE_READ,
|
||||
NULL, OPEN_EXISTING, 0, NULL);
|
||||
if(file == INVALID_HANDLE_VALUE)
|
||||
return ret;
|
||||
|
||||
map = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||
if(!map)
|
||||
{
|
||||
CloseHandle(file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
base = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
|
||||
if(!base)
|
||||
{
|
||||
CloseHandle(map);
|
||||
CloseHandle(file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Sanity checks */
|
||||
dos = (PIMAGE_DOS_HEADER)base;
|
||||
nt = (PIMAGE_NT_HEADERS)((char *)base + dos->e_lfanew);
|
||||
if(dos->e_magic == IMAGE_DOS_SIGNATURE /* 0x5A4D */
|
||||
&& nt->Signature == IMAGE_NT_SIGNATURE /* 0x00004550 */
|
||||
&& nt->FileHeader.Machine == IMAGE_FILE_MACHINE_I386
|
||||
&& nt->OptionalHeader.Magic == 0x10b /* IMAGE_NT_OPTIONAL_HDR32_MAGIC */)
|
||||
{
|
||||
ret = get_base_address(pid);
|
||||
/* Base address not found in the running process. Falling back
|
||||
* to the header's information, which is unreliable because of
|
||||
* Vista's address space randomisation. */
|
||||
if (!ret)
|
||||
ret = (intptr_t)nt->OptionalHeader.ImageBase;
|
||||
|
||||
ret += (intptr_t)nt->OptionalHeader.AddressOfEntryPoint;
|
||||
}
|
||||
|
||||
UnmapViewOfFile(base);
|
||||
CloseHandle(map);
|
||||
CloseHandle(file);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* FIXME: this could probably be merged with get_entry_point */
|
||||
static intptr_t get_proc_address(void *process, DWORD pid, const char *func)
|
||||
{
|
||||
char buf[1024];
|
||||
size_t buflen = strlen(func) + 1;
|
||||
|
||||
MODULEENTRY32 entry;
|
||||
intptr_t ret = 0;
|
||||
DWORD tmp;
|
||||
void *list;
|
||||
int i, k;
|
||||
|
||||
list = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
|
||||
entry.dwSize = sizeof(entry);
|
||||
for(k = Module32First(list, &entry); k; k = Module32Next(list, &entry))
|
||||
{
|
||||
IMAGE_DOS_HEADER dos;
|
||||
IMAGE_NT_HEADERS nt;
|
||||
IMAGE_EXPORT_DIRECTORY expdir;
|
||||
|
||||
uint32_t exportaddr;
|
||||
uint8_t const *base = entry.modBaseAddr;
|
||||
|
||||
if (strcmp("kernel32.dll", entry.szModule))
|
||||
continue;
|
||||
|
||||
ReadProcessMemory(process, base, &dos, sizeof(dos), &tmp);
|
||||
ReadProcessMemory(process, base + dos.e_lfanew, &nt, sizeof(nt), &tmp);
|
||||
|
||||
exportaddr = nt.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
|
||||
if (!exportaddr)
|
||||
continue;
|
||||
|
||||
ReadProcessMemory(process, base + exportaddr, &expdir, sizeof(expdir), &tmp);
|
||||
|
||||
for (i = 0; i < (int)expdir.NumberOfNames; i++)
|
||||
{
|
||||
uint32_t nameaddr, funcaddr;
|
||||
uint16_t j;
|
||||
|
||||
/* Look for our function name in the list of names */
|
||||
ReadProcessMemory(process, base + expdir.AddressOfNames
|
||||
+ i * sizeof(DWORD),
|
||||
&nameaddr, sizeof(nameaddr), &tmp);
|
||||
ReadProcessMemory(process, base + nameaddr, buf, buflen, &tmp);
|
||||
|
||||
if (strcmp(buf, func))
|
||||
continue;
|
||||
|
||||
/* If we found a function with this name, return its address */
|
||||
ReadProcessMemory(process, base + expdir.AddressOfNameOrdinals
|
||||
+ i * sizeof(WORD),
|
||||
&j, sizeof(j), &tmp);
|
||||
ReadProcessMemory(process, base + expdir.AddressOfFunctions
|
||||
+ j * sizeof(DWORD),
|
||||
&funcaddr, sizeof(funcaddr), &tmp);
|
||||
|
||||
ret = (intptr_t)base + funcaddr;
|
||||
goto _finished;
|
||||
}
|
||||
}
|
||||
|
||||
_finished:
|
||||
CloseHandle(list);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Find the process's base address once it is loaded in memory (the header
|
||||
* information is unreliable because of Vista's ASLR).
|
||||
* FIXME: this does not work properly because CreateToolhelp32Snapshot()
|
||||
* requires a certain level of initialisation. */
|
||||
static intptr_t get_base_address(DWORD pid)
|
||||
{
|
||||
MODULEENTRY32 entry;
|
||||
intptr_t ret = 0;
|
||||
|
||||
DWORD tmp;
|
||||
void *list;
|
||||
int k;
|
||||
int i, k;
|
||||
|
||||
list = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
|
||||
entry.dwSize = sizeof(entry);
|
||||
for(k = Module32First(list, &entry); k; k = Module32Next(list, &entry))
|
||||
{
|
||||
/* FIXME: how do we select the correct module? */
|
||||
ret = (intptr_t)entry.modBaseAddr;
|
||||
}
|
||||
CloseHandle(list);
|
||||
IMAGE_DOS_HEADER dos;
|
||||
IMAGE_NT_HEADERS nt;
|
||||
IMAGE_EXPORT_DIRECTORY expdir;
|
||||
|
||||
uint32_t exportaddr;
|
||||
uint8_t const *base = entry.modBaseAddr;
|
||||
|
||||
if (strcmp("kernel32.dll", entry.szModule))
|
||||
continue;
|
||||
|
||||
ReadProcessMemory(process, base, &dos, sizeof(dos), &tmp);
|
||||
ReadProcessMemory(process, base + dos.e_lfanew, &nt, sizeof(nt), &tmp);
|
||||
|
||||
exportaddr = nt.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
|
||||
if (!exportaddr)
|
||||
continue;
|
||||
|
||||
ReadProcessMemory(process, base + exportaddr, &expdir, sizeof(expdir), &tmp);
|
||||
|
||||
for (i = 0; i < (int)expdir.NumberOfNames; i++)
|
||||
{
|
||||
uint32_t nameaddr, funcaddr;
|
||||
uint16_t j;
|
||||
|
||||
/* Look for our function name in the list of names */
|
||||
ReadProcessMemory(process, base + expdir.AddressOfNames
|
||||
+ i * sizeof(DWORD),
|
||||
&nameaddr, sizeof(nameaddr), &tmp);
|
||||
ReadProcessMemory(process, base + nameaddr, buf, buflen, &tmp);
|
||||
|
||||
if (strcmp(buf, func))
|
||||
continue;
|
||||
|
||||
/* If we found a function with this name, return its address */
|
||||
ReadProcessMemory(process, base + expdir.AddressOfNameOrdinals
|
||||
+ i * sizeof(WORD),
|
||||
&j, sizeof(j), &tmp);
|
||||
ReadProcessMemory(process, base + expdir.AddressOfFunctions
|
||||
+ j * sizeof(DWORD),
|
||||
&funcaddr, sizeof(funcaddr), &tmp);
|
||||
|
||||
ret = (intptr_t)base + funcaddr;
|
||||
goto _finished;
|
||||
}
|
||||
}
|
||||
|
||||
_finished:
|
||||
CloseHandle(list);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user