Inject by remote load if there's no IAT on Win8+
Windows 8 and later require the IDT to be within a section when there's no IAT. This prevents relocated imports from working, so we cannot add ourself to the import table. Use `LdrLoadDll` via `CreateRemoteThread` for such a situation.
This commit is contained in:
parent
f4697b59fa
commit
f8509c916c
21
ANSI.c
21
ANSI.c
@ -197,13 +197,14 @@
|
|||||||
v1.83, 16 February, 2018:
|
v1.83, 16 February, 2018:
|
||||||
create the flush thread on first use.
|
create the flush thread on first use.
|
||||||
|
|
||||||
v1.84-wip, 17 February, 26 April to 2 May, 2018:
|
v1.84-wip, 17 February, 26 April to 4 May, 2018:
|
||||||
close the flush handles on detach;
|
close the flush handles on detach;
|
||||||
dynamically load WINMM.DLL;
|
dynamically load WINMM.DLL;
|
||||||
use sprintf/_snprintf/_snwprintf instead of wsprintf, avoiding USER32.DLL;
|
use sprintf/_snprintf/_snwprintf instead of wsprintf, avoiding USER32.DLL;
|
||||||
replace bsearch (in procrva.c) with specific code;
|
replace bsearch (in procrva.c) with specific code;
|
||||||
if the primary thread is detached exit the process;
|
if the primary thread is detached exit the process;
|
||||||
get real WriteFile handle before testing for console.
|
get real WriteFile handle before testing for console;
|
||||||
|
use remote load on Win8+ when the process has no IAT.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ansicon.h"
|
#include "ansicon.h"
|
||||||
@ -2984,7 +2985,7 @@ void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi,
|
|||||||
}
|
}
|
||||||
else // (type == 48)
|
else // (type == 48)
|
||||||
{
|
{
|
||||||
InjectDLL64( child_pi );
|
RemoteLoad64( child_pi );
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#ifdef W32ON64
|
#ifdef W32ON64
|
||||||
@ -3840,16 +3841,24 @@ void OriginalAttr( PVOID lpReserved )
|
|||||||
|
|
||||||
// If we were loaded dynamically, remember the current attributes to restore
|
// If we were loaded dynamically, remember the current attributes to restore
|
||||||
// upon unloading. However, if we're the 64-bit DLL, but the image is 32-
|
// upon unloading. However, if we're the 64-bit DLL, but the image is 32-
|
||||||
// bit, then the dynamic load was due to injecting into AnyCPU.
|
// bit, then the dynamic load was due to injecting into AnyCPU. It may also
|
||||||
|
// be dynamic due to lack of the IAT.
|
||||||
if (lpReserved == NULL)
|
if (lpReserved == NULL)
|
||||||
{
|
{
|
||||||
#ifdef _WIN64
|
BOOL dynamic = TRUE;
|
||||||
PIMAGE_DOS_HEADER pDosHeader;
|
PIMAGE_DOS_HEADER pDosHeader;
|
||||||
PIMAGE_NT_HEADERS pNTHeader;
|
PIMAGE_NT_HEADERS pNTHeader;
|
||||||
pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandle( NULL );
|
pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandle( NULL );
|
||||||
pNTHeader = MakeVA( PIMAGE_NT_HEADERS, pDosHeader->e_lfanew );
|
pNTHeader = MakeVA( PIMAGE_NT_HEADERS, pDosHeader->e_lfanew );
|
||||||
if (pNTHeader->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
|
#ifdef _WIN64
|
||||||
|
if (pNTHeader->FileHeader.Machine == IMAGE_FILE_MACHINE_I386)
|
||||||
|
dynamic = FALSE;
|
||||||
|
else
|
||||||
#endif
|
#endif
|
||||||
|
if (pNTHeader->DATADIRS <= IMAGE_DIRECTORY_ENTRY_IAT &&
|
||||||
|
get_os_version() >= 0x602)
|
||||||
|
dynamic = FALSE;
|
||||||
|
if (dynamic)
|
||||||
orgattr = ATTR;
|
orgattr = ATTR;
|
||||||
GetConsoleMode( hConOut, &orgmode );
|
GetConsoleMode( hConOut, &orgmode );
|
||||||
GetConsoleCursorInfo( hConOut, &orgcci );
|
GetConsoleCursorInfo( hConOut, &orgcci );
|
||||||
|
@ -91,7 +91,7 @@
|
|||||||
use -pu to unload from the parent.
|
use -pu to unload from the parent.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define PDATE L"30 April, 2018"
|
#define PDATE L"4 May, 2018"
|
||||||
|
|
||||||
#include "ansicon.h"
|
#include "ansicon.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
@ -208,7 +208,7 @@ BOOL Inject( LPPROCESS_INFORMATION ppi, BOOL* gui, LPCTSTR app )
|
|||||||
else if (type == 32)
|
else if (type == 32)
|
||||||
InjectDLL32( ppi, base );
|
InjectDLL32( ppi, base );
|
||||||
else // (type == 48)
|
else // (type == 48)
|
||||||
InjectDLL64( ppi );
|
RemoteLoad64( ppi );
|
||||||
#else
|
#else
|
||||||
wcscpy( DllName + len, L"ANSI32.dll" );
|
wcscpy( DllName + len, L"ANSI32.dll" );
|
||||||
set_ansi_dll();
|
set_ansi_dll();
|
||||||
|
@ -87,9 +87,10 @@ int ProcessType( LPPROCESS_INFORMATION, PBYTE*, BOOL* );
|
|||||||
BOOL Wow64Process( HANDLE );
|
BOOL Wow64Process( HANDLE );
|
||||||
|
|
||||||
void InjectDLL( LPPROCESS_INFORMATION, PBYTE );
|
void InjectDLL( LPPROCESS_INFORMATION, PBYTE );
|
||||||
|
void RemoteLoad32( LPPROCESS_INFORMATION );
|
||||||
#ifdef _WIN64
|
#ifdef _WIN64
|
||||||
void InjectDLL32( LPPROCESS_INFORMATION, PBYTE );
|
void InjectDLL32( LPPROCESS_INFORMATION, PBYTE );
|
||||||
void InjectDLL64( LPPROCESS_INFORMATION );
|
void RemoteLoad64( LPPROCESS_INFORMATION );
|
||||||
DWORD GetProcRVA( LPCTSTR, LPCSTR, int );
|
DWORD GetProcRVA( LPCTSTR, LPCSTR, int );
|
||||||
#else
|
#else
|
||||||
DWORD GetProcRVA( LPCTSTR, LPCSTR );
|
DWORD GetProcRVA( LPCTSTR, LPCSTR );
|
||||||
@ -107,6 +108,7 @@ extern char ansi_dll[MAX_PATH];
|
|||||||
extern DWORD ansi_len;
|
extern DWORD ansi_len;
|
||||||
extern char* ansi_bits;
|
extern char* ansi_bits;
|
||||||
void set_ansi_dll( void );
|
void set_ansi_dll( void );
|
||||||
|
DWORD get_os_version( void );
|
||||||
|
|
||||||
extern int log_level;
|
extern int log_level;
|
||||||
void DEBUGSTR( int level, LPCSTR szFormat, ... );
|
void DEBUGSTR( int level, LPCSTR szFormat, ... );
|
||||||
|
110
injdll.c
110
injdll.c
@ -84,6 +84,19 @@ void InjectDLL( LPPROCESS_INFORMATION ppi, PBYTE pBase )
|
|||||||
pNTHeader = (PIMAGE_NT_HEADERS)(pBase + DosHeader.e_lfanew);
|
pNTHeader = (PIMAGE_NT_HEADERS)(pBase + DosHeader.e_lfanew);
|
||||||
ReadProcVar( pNTHeader, &NTHeader );
|
ReadProcVar( pNTHeader, &NTHeader );
|
||||||
|
|
||||||
|
// Windows 8 and later require the IDT to be part of a section when there's
|
||||||
|
// no IAT. This means we can't move the imports, so remote load instead.
|
||||||
|
if (NTHeader.DATADIRS <= IMAGE_DIRECTORY_ENTRY_IAT &&
|
||||||
|
get_os_version() >= 0x602)
|
||||||
|
{
|
||||||
|
#ifdef _WIN64
|
||||||
|
RemoteLoad64( ppi );
|
||||||
|
#else
|
||||||
|
RemoteLoad32( ppi );
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
import_size = sizeof_imports( ppi, pBase, NTHeader.IMPORTDIR.VirtualAddress );
|
import_size = sizeof_imports( ppi, pBase, NTHeader.IMPORTDIR.VirtualAddress );
|
||||||
len = 2 * PTRSZ + ansi_len + sizeof(*pImports) + import_size;
|
len = 2 * PTRSZ + ansi_len + sizeof(*pImports) + import_size;
|
||||||
pImports = HeapAlloc( hHeap, 0, len );
|
pImports = HeapAlloc( hHeap, 0, len );
|
||||||
@ -173,6 +186,13 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, PBYTE pBase )
|
|||||||
pNTHeader = (PIMAGE_NT_HEADERS32)(pBase + DosHeader.e_lfanew);
|
pNTHeader = (PIMAGE_NT_HEADERS32)(pBase + DosHeader.e_lfanew);
|
||||||
ReadProcVar( pNTHeader, &NTHeader );
|
ReadProcVar( pNTHeader, &NTHeader );
|
||||||
|
|
||||||
|
if (NTHeader.DATADIRS <= IMAGE_DIRECTORY_ENTRY_IAT &&
|
||||||
|
get_os_version() >= 0x602)
|
||||||
|
{
|
||||||
|
RemoteLoad32( ppi );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
import_size = sizeof_imports( ppi, pBase, NTHeader.IMPORTDIR.VirtualAddress );
|
import_size = sizeof_imports( ppi, pBase, NTHeader.IMPORTDIR.VirtualAddress );
|
||||||
len = 8 + ansi_len + sizeof(*pImports) + import_size;
|
len = 8 + ansi_len + sizeof(*pImports) + import_size;
|
||||||
pImports = HeapAlloc( hHeap, 0, len );
|
pImports = HeapAlloc( hHeap, 0, len );
|
||||||
@ -232,16 +252,21 @@ void InjectDLL32( LPPROCESS_INFORMATION ppi, PBYTE pBase )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Locate the base address of 64-bit ntdll.dll. This is supposedly really at
|
Locate the base address of ntdll.dll. This is supposedly really at the same
|
||||||
the same address for every process, but let's find it anyway. A newly-
|
address for every process, but let's find it anyway. A newly-created
|
||||||
created suspended 64-bit process has two images in memory: the process itself
|
suspended process has two images in memory: the process itself and ntdll.dll.
|
||||||
and ntdll.dll - the one that is a DLL must be ntdll.dll. (A 32-bit WOW64
|
Thus the one that is a DLL must be ntdll.dll. However, a WOW64 process also
|
||||||
process has three images - the process and both 64- & 32-bit ntdll.dll).
|
has the 64-bit version, so test the machine.
|
||||||
*/
|
*/
|
||||||
|
#ifdef _WIN64
|
||||||
|
static PBYTE get_ntdll( LPPROCESS_INFORMATION ppi, WORD machine )
|
||||||
|
#else
|
||||||
static PBYTE get_ntdll( LPPROCESS_INFORMATION ppi )
|
static PBYTE get_ntdll( LPPROCESS_INFORMATION ppi )
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
PBYTE ptr;
|
PBYTE ptr;
|
||||||
MEMORY_BASIC_INFORMATION minfo;
|
MEMORY_BASIC_INFORMATION minfo;
|
||||||
@ -258,7 +283,11 @@ static PBYTE get_ntdll( LPPROCESS_INFORMATION ppi )
|
|||||||
&& ReadProcVar( (PBYTE)minfo.BaseAddress + dos_header.e_lfanew,
|
&& ReadProcVar( (PBYTE)minfo.BaseAddress + dos_header.e_lfanew,
|
||||||
&nt_header )
|
&nt_header )
|
||||||
&& nt_header.Signature == IMAGE_NT_SIGNATURE
|
&& nt_header.Signature == IMAGE_NT_SIGNATURE
|
||||||
&& (nt_header.FileHeader.Characteristics & IMAGE_FILE_DLL))
|
&& (nt_header.FileHeader.Characteristics & IMAGE_FILE_DLL)
|
||||||
|
#ifdef _WIN64
|
||||||
|
&& nt_header.FileHeader.Machine == machine
|
||||||
|
#endif
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return minfo.BaseAddress;
|
return minfo.BaseAddress;
|
||||||
}
|
}
|
||||||
@ -269,7 +298,8 @@ static PBYTE get_ntdll( LPPROCESS_INFORMATION ppi )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void InjectDLL64( LPPROCESS_INFORMATION ppi )
|
#ifdef _WIN64
|
||||||
|
void RemoteLoad64( LPPROCESS_INFORMATION ppi )
|
||||||
{
|
{
|
||||||
PBYTE ntdll;
|
PBYTE ntdll;
|
||||||
DWORD rLdrLoadDll;
|
DWORD rLdrLoadDll;
|
||||||
@ -285,7 +315,7 @@ void InjectDLL64( LPPROCESS_INFORMATION ppi )
|
|||||||
PBYTE* pL;
|
PBYTE* pL;
|
||||||
} ip;
|
} ip;
|
||||||
|
|
||||||
ntdll = get_ntdll( ppi );
|
ntdll = get_ntdll( ppi, IMAGE_FILE_MACHINE_AMD64 );
|
||||||
if (ntdll == NULL)
|
if (ntdll == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -327,3 +357,67 @@ void InjectDLL64( LPPROCESS_INFORMATION ppi )
|
|||||||
VirtualFreeEx( ppi->hProcess, pMem, 0, MEM_RELEASE );
|
VirtualFreeEx( ppi->hProcess, pMem, 0, MEM_RELEASE );
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
void RemoteLoad32( LPPROCESS_INFORMATION ppi )
|
||||||
|
{
|
||||||
|
PBYTE ntdll;
|
||||||
|
DWORD rLdrLoadDll;
|
||||||
|
PBYTE pMem;
|
||||||
|
DWORD bMem;
|
||||||
|
DWORD len;
|
||||||
|
HANDLE thread;
|
||||||
|
BYTE code[64];
|
||||||
|
union
|
||||||
|
{
|
||||||
|
PBYTE pB;
|
||||||
|
PUSHORT pS;
|
||||||
|
PDWORD pD;
|
||||||
|
} ip;
|
||||||
|
|
||||||
|
#ifdef _WIN64
|
||||||
|
ntdll = get_ntdll( ppi, IMAGE_FILE_MACHINE_I386 );
|
||||||
|
#else
|
||||||
|
ntdll = get_ntdll( ppi );
|
||||||
|
#endif
|
||||||
|
if (ntdll == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
#ifdef _WIN64
|
||||||
|
rLdrLoadDll = GetProcRVA( L"ntdll.dll", "LdrLoadDll", 32 );
|
||||||
|
#else
|
||||||
|
rLdrLoadDll = GetProcRVA( L"ntdll.dll", "LdrLoadDll" );
|
||||||
|
#endif
|
||||||
|
if (rLdrLoadDll == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pMem = VirtualAllocEx( ppi->hProcess, NULL, 4096, MEM_COMMIT,
|
||||||
|
PAGE_EXECUTE_READ );
|
||||||
|
if (pMem == NULL)
|
||||||
|
{
|
||||||
|
DEBUGSTR(1, " Failed to allocate virtual memory (%u)", GetLastError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bMem = PtrToUint( pMem );
|
||||||
|
|
||||||
|
len = (DWORD)TSIZE(wcslen( DllName ) + 1);
|
||||||
|
ip.pB = code;
|
||||||
|
|
||||||
|
*ip.pS++ = 0x5451; // push ecx esp
|
||||||
|
*ip.pB++ = 0x68; // push
|
||||||
|
*ip.pD++ = bMem + 20; // L"path\to\ANSI32.dll"
|
||||||
|
*ip.pD++ = 0x006A006A; // push 0 0
|
||||||
|
*ip.pB++ = 0xe8; // call LdrLoadDll
|
||||||
|
*ip.pD++ = PtrToUint( ntdll ) + rLdrLoadDll - (bMem + 16);
|
||||||
|
*ip.pD++ = 0xc359; // pop ecx / ret and padding
|
||||||
|
*ip.pS++ = (USHORT)(len - TSIZE(1)); // UNICODE_STRING.Length
|
||||||
|
*ip.pS++ = (USHORT)len; // UNICODE_STRING.MaximumLength
|
||||||
|
*ip.pD++ = bMem + 28; // UNICODE_STRING.Buffer
|
||||||
|
WriteProcMem( pMem, code, ip.pB - code );
|
||||||
|
WriteProcMem( pMem + (ip.pB - code), DllName, len );
|
||||||
|
thread = CreateRemoteThread( ppi->hProcess, NULL, 4096,
|
||||||
|
(LPTHREAD_START_ROUTINE)pMem, NULL, 0, NULL );
|
||||||
|
WaitForSingleObject( thread, INFINITE );
|
||||||
|
CloseHandle( thread );
|
||||||
|
VirtualFreeEx( ppi->hProcess, pMem, 0, MEM_RELEASE );
|
||||||
|
}
|
||||||
|
@ -70,9 +70,9 @@ LIBS = advapi32.lib $(LIBS64)
|
|||||||
# Identify ansicon.exe using "ANSI" as a version number.
|
# Identify ansicon.exe using "ANSI" as a version number.
|
||||||
LINK = /link /version:20033.18771
|
LINK = /link /version:20033.18771
|
||||||
|
|
||||||
X86OBJS = x86\injdll.obj x86\proctype.obj x86\util.obj
|
X86OBJS = x86\injdll.obj x86\procrva.obj x86\proctype.obj x86\util.obj
|
||||||
X64OBJS = x64\injdll.obj x64\proctype.obj x64\util.obj x64\procrva.obj
|
X64OBJS = x64\injdll.obj x64\procrva.obj x64\proctype.obj x64\util.obj
|
||||||
X6432OBJS = x86\injdll.obj x64\proctype32.obj x86\util.obj
|
X6432OBJS = x86\injdll.obj x86\procrva.obj x64\proctype32.obj x86\util.obj
|
||||||
|
|
||||||
!IF !DEFINED(V)
|
!IF !DEFINED(V)
|
||||||
V = 0
|
V = 0
|
||||||
@ -99,7 +99,7 @@ ansicon64: x64 x64\ansicon.exe x64\ANSI64.dll
|
|||||||
x86:
|
x86:
|
||||||
mkdir x86
|
mkdir x86
|
||||||
|
|
||||||
x86\ansicon.exe: x86\ansicon.obj $(X86OBJS) x86\procrva.obj x86\ansicon.res
|
x86\ansicon.exe: x86\ansicon.obj $(X86OBJS) x86\ansicon.res
|
||||||
$(LDmsg)$(CC) /nologo $(SHARE) /Fe$@ $** $(LIBS) $(LINK) /filealign:512
|
$(LDmsg)$(CC) /nologo $(SHARE) /Fe$@ $** $(LIBS) $(LINK) /filealign:512
|
||||||
!IF "$(_NMAKE_VER)" == "9.00.30729.01"
|
!IF "$(_NMAKE_VER)" == "9.00.30729.01"
|
||||||
$(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;1
|
$(MTmsg)$(MT) /nologo -manifest $@.manifest -outputresource:$@;1
|
||||||
|
@ -339,10 +339,10 @@ Version History
|
|||||||
|
|
||||||
Legend: + added, - bug-fixed, * changed.
|
Legend: + added, - bug-fixed, * changed.
|
||||||
|
|
||||||
1.84-wip - 3 May, 2018:
|
1.84-wip - 4 May, 2018:
|
||||||
- close the flush handles on detach;
|
- close the flush handles on detach;
|
||||||
- use remote load on Win8+ if the process has no IAT;
|
|
||||||
- WriteFile wasn't properly testing if its handle was for a console;
|
- WriteFile wasn't properly testing if its handle was for a console;
|
||||||
|
- use remote load on Win8+ if the process has no IAT;
|
||||||
* remove dependency on USER32, dynamically load WINMM;
|
* remove dependency on USER32, dynamically load WINMM;
|
||||||
* exit process if the primary thread is detached (for processes on Win10
|
* exit process if the primary thread is detached (for processes on Win10
|
||||||
that return, rather than call ExitProcess).
|
that return, rather than call ExitProcess).
|
||||||
@ -621,4 +621,4 @@ Distribution
|
|||||||
|
|
||||||
|
|
||||||
========================
|
========================
|
||||||
Jason Hood, 3 May, 2018.
|
Jason Hood, 4 May, 2018.
|
||||||
|
14
util.c
14
util.c
@ -74,6 +74,20 @@ void set_ansi_dll( void )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// GetVersion and GetVersionEx use Win32VersionValue from the header, which
|
||||||
|
// could be anything. Retrieve the OS version from NTDLL's header.
|
||||||
|
DWORD get_os_version( void )
|
||||||
|
{
|
||||||
|
PIMAGE_DOS_HEADER pDosHeader;
|
||||||
|
PIMAGE_NT_HEADERS pNTHeader;
|
||||||
|
|
||||||
|
pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandle( L"ntdll.dll" );
|
||||||
|
pNTHeader = MakeVA( PIMAGE_NT_HEADERS, pDosHeader->e_lfanew );
|
||||||
|
return pNTHeader->OptionalHeader.MajorOperatingSystemVersion << 8 |
|
||||||
|
pNTHeader->OptionalHeader.MinorOperatingSystemVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static LPSTR buf;
|
static LPSTR buf;
|
||||||
static DWORD buf_len;
|
static DWORD buf_len;
|
||||||
static BOOL quote, alt;
|
static BOOL quote, alt;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user