when CTRL+C是控制台进程的输入,系统在该进程中创建具有入口点的线程
EXTERN_C
WINBASEAPI
ULONG
WINAPI
CtrlRoutine(_In_ DWORD dwCtrlEvent);
该函数由导出内核32.dll(可以转发导出到另一个 dll,例如内核库.dll)
this CtrlRoutine
接下来做:
如果正在调试进程 - raiseDBG_CONTROL_C https://learn.microsoft.com/en-us/windows/win32/debug/debugging-events例外,那么
调用 注册者SetConsoleCtrlHandler
回调。如果没有注册回调或全部返回 false -DefaultHandler
被调用,它只是调用ExitProcess(STATUS_CONTROL_C_EXIT)
(通过 CTRL+C 退出应用程序)
但可以自己直接打电话CreateRemoteThread
在目标进程中,入口点位于CtrlRoutine
and CTRL_C_EVENT
作为参数。如果目标进程具有与我们相同的数字容量 - 32 位或 64 位 - 没有任何问题 - 我们可以导入CtrlRoutine
已经在链接时(它定义在内核32.lib)或通过获取它的地址GetProcAddress
。但是如果我们的进程是 64 位本机并且目标进程是 32 位(WoW64) - 这里的问题 - 我们需要获取地址CtrlRoutine
inside 32-bit 内核32.dll- 但我们不能直接在自己的64位进程中加载它并调用GetProcAddress
。需要自己映射这个dll并自己获取它的基础并解析导出。这个任务已经不简单了。如果我们在 wow64 进程(64 位 Windows 上的 32 位进程)中运行并且目标进程是 64 位(本机) - 任务已经变得非常困难(尽管也存在解决方案,但无需创建额外的进程)。但我假设控制进程是本机的(64 位 Windows 上的 64 位)
#pragma warning( disable : 4201)
#include <Windows.h>
#include <malloc.h>
#define LDR_DATA_TABLE_ENTRY _LDR_DATA_TABLE_ENTRY_
#define PLDR_DATA_TABLE_ENTRY _PLDR_DATA_TABLE_ENTRY_
#include <winternl.h>
#undef PLDR_DATA_TABLE_ENTRY
#undef LDR_DATA_TABLE_ENTRY
#define MAXUSHORT 0xffff
#define MAXULONG 0xffffffff
#define RtlOffsetToPointer(B,O) ((PCHAR)( ((PCHAR)(B)) + ((ULONG_PTR)(O)) ))
#define RtlPointerToOffset(B,P) ((ULONG)( ((PCHAR)(P)) - ((PCHAR)(B)) ))
#define RTL_CONSTANT_STRING(s) { sizeof( s ) - sizeof( (s)[0] ), sizeof( s ), const_cast<PWSTR>(s) }
#define NtCurrentProcess() ( (HANDLE)(LONG_PTR) -1 )
typedef enum _SECTION_INHERIT {
ViewShare = 1,
ViewUnmap = 2
} SECTION_INHERIT;
typedef enum _SECTION_INFORMATION_CLASS
{
SectionBasicInformation, // q; SECTION_BASIC_INFORMATION
SectionImageInformation, // q; SECTION_IMAGE_INFORMATION
SectionRelocationInformation, // name:wow64:whNtQuerySection_SectionRelocationInformation
SectionOriginalBaseInformation, // PVOID BaseAddress
SectionInternalImageInformation, // SECTION_INTERNAL_IMAGE_INFORMATION // since REDSTONE2
MaxSectionInfoClass
} SECTION_INFORMATION_CLASS;
typedef struct SECTION_IMAGE_INFORMATION
{
PVOID TransferAddress;
ULONG ZeroBits;
SIZE_T MaximumStackSize;
SIZE_T CommittedStackSize;
ULONG SubSystemType;
union
{
struct
{
USHORT SubSystemMinorVersion;
USHORT SubSystemMajorVersion;
};
ULONG SubSystemVersion;
};
union
{
struct
{
USHORT MajorOperatingSystemVersion;
USHORT MinorOperatingSystemVersion;
};
ULONG OperatingSystemVersion;
};
USHORT ImageCharacteristics;
USHORT DllCharacteristics;
USHORT Machine;
BOOLEAN ImageContainsCode;
union
{
UCHAR ImageFlags;
struct
{
UCHAR ComPlusNativeReady : 1;
UCHAR ComPlusILOnly : 1;
UCHAR ImageDynamicallyRelocated : 1;
UCHAR ImageMappedFlat : 1;
UCHAR BaseBelow4gb : 1;
UCHAR ComPlusPrefer32bit : 1;
UCHAR Reserved : 2;
};
};
ULONG LoaderFlags;
ULONG ImageFileSize;
ULONG CheckSum;
} *PSECTION_IMAGE_INFORMATION;
typedef struct LDR_DATA_TABLE_ENTRY
{
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
void *DllBase;
void *EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
// .. more members. trucated
} *PLDR_DATA_TABLE_ENTRY;
EXTERN_C_START
NTSYSAPI
NTSTATUS
NTAPI
LdrFindEntryForAddress(
_In_ PVOID DllHandle,
_Out_ PLDR_DATA_TABLE_ENTRY *Entry
);
NTSYSAPI
PIMAGE_NT_HEADERS
NTAPI
RtlImageNtHeader(
_In_ PVOID Base
);
EXTERN_C
NTSYSAPI
PVOID
NTAPI
RtlImageDirectoryEntryToData(
_In_ PVOID Base,
_In_ BOOLEAN MappedAsImage,
_In_ USHORT DirectoryEntry,
_Out_ PULONG Size
);
NTSYSAPI
NTSTATUS
NTAPI
RtlMultiByteToUnicodeSize(
_Out_ PULONG BytesInUnicodeString,
_In_reads_bytes_(BytesInMultiByteString) const CHAR *MultiByteString,
_In_ ULONG BytesInMultiByteString
);
NTSYSAPI
NTSTATUS
NTAPI
RtlMultiByteToUnicodeSize(
_Out_ PULONG BytesInUnicodeString,
_In_reads_bytes_(BytesInMultiByteString) const CHAR *MultiByteString,
_In_ ULONG BytesInMultiByteString
);
NTSYSAPI
NTSTATUS
NTAPI
RtlMultiByteToUnicodeN(
_Out_writes_bytes_to_(MaxBytesInUnicodeString, *BytesInUnicodeString) PWCH UnicodeString,
_In_ ULONG MaxBytesInUnicodeString,
_Out_opt_ PULONG BytesInUnicodeString,
_In_reads_bytes_(BytesInMultiByteString) const CHAR *MultiByteString,
_In_ ULONG BytesInMultiByteString
);
NTSYSAPI
NTSTATUS
NTAPI
RtlAppendUnicodeToString (
_Inout_ PUNICODE_STRING Destination,
_In_opt_z_ PCWSTR Source
);
NTSYSAPI
NTSTATUS
NTAPI
RtlAppendUnicodeStringToString (
_Inout_ PUNICODE_STRING Destination,
_In_ PCUNICODE_STRING Source
);
NTSYSAPI
BOOLEAN
NTAPI
RtlPrefixUnicodeString(
_In_ PCUNICODE_STRING String1,
_In_ PCUNICODE_STRING String2,
_In_ BOOLEAN CaseInSensitive
);
NTSYSAPI
NTSTATUS
NTAPI
ZwUnmapViewOfSection(
_In_ HANDLE ProcessHandle,
_In_opt_ PVOID BaseAddress
);
NTSYSAPI
NTSTATUS
NTAPI
ZwMapViewOfSection(
_In_ HANDLE SectionHandle,
_In_ HANDLE ProcessHandle,
_Outptr_result_bytebuffer_(*ViewSize) PVOID *BaseAddress,
_In_ ULONG_PTR ZeroBits,
_In_ SIZE_T CommitSize,
_Inout_opt_ PLARGE_INTEGER SectionOffset,
_Inout_ PSIZE_T ViewSize,
_In_ SECTION_INHERIT InheritDisposition,
_In_ ULONG AllocationType,
_In_ ULONG Win32Protect
);
NTSYSAPI
NTSTATUS
NTAPI
ZwOpenSection(
_Out_ PHANDLE SectionHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes
);
NTSYSAPI
NTSTATUS
NTAPI
ZwQuerySection(
_In_ HANDLE SectionHandle,
_In_ ULONG SectionInformationClass,
_Out_ PVOID SectionInformation,
_In_ ULONG SectionInformationLength,
_Out_ PSIZE_T ResultLength OPTIONAL
);
NTSYSAPI
NTSTATUS
NTAPI
LdrLoadDll(
_In_opt_ PWSTR DllPath,
_In_opt_ PULONG DllCharacteristics,
_In_ PUNICODE_STRING DllName,
_Out_ HMODULE *DllHandle
);
NTSYSAPI
NTSTATUS
NTAPI
LdrUnloadDll(
_In_ PVOID DllHandle
);
NTSYSAPI
NTSTATUS
NTAPI
LdrGetProcedureAddress(
_In_ PVOID DllHandle,
_In_opt_ PANSI_STRING ProcedureName,
_In_opt_ ULONG ProcedureNumber,
_Out_ PVOID *ProcedureAddress
);
EXTERN_C_END
ULONG GetNameOrdinal(PVOID Base, PDWORD AddressOfNames, DWORD NumberOfNames, PCSTR Name)
{
if (NumberOfNames)
{
DWORD a = 0, o;
do
{
o = (a + NumberOfNames) >> 1;
int i = strcmp(RtlOffsetToPointer(Base, AddressOfNames[o]), Name);
if (!i)
{
return o;
}
0 > i ? a = o + 1 : NumberOfNames = o;
} while (a < NumberOfNames);
}
return MAXULONG;
}
PVOID getWowProcs(PCUNICODE_STRING DllName, PCSTR Name);
PVOID getWowProcs(PVOID ImageBase, PVOID BaseAddress, PCSTR Name)
{
ULONG exportSize, exportRVA;
PIMAGE_EXPORT_DIRECTORY pied = (PIMAGE_EXPORT_DIRECTORY)
RtlImageDirectoryEntryToData(BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &exportSize);
if (!pied) return 0;
exportRVA = RtlPointerToOffset(BaseAddress, pied);
ULONG NumberOfFunctions = pied->NumberOfFunctions;
if (!NumberOfFunctions) return 0;
ULONG NumberOfNames = pied->NumberOfNames;
ULONG OrdinalBase = pied->Base;
PULONG AddressOfFunctions = (PULONG)RtlOffsetToPointer(BaseAddress, pied->AddressOfFunctions);
PULONG AddressOfNames = (PULONG)RtlOffsetToPointer(BaseAddress, pied->AddressOfNames);
PWORD AddressOfNameOrdinals = (PWORD)RtlOffsetToPointer(BaseAddress, pied->AddressOfNameOrdinals);
ULONG o;
if (*Name == '#')
{
if ((o = strtoul(Name + 1, const_cast<char**>(&Name), 10)) < OrdinalBase || *Name)
{
return 0;
}
o -= OrdinalBase;
}
else
{
o = GetNameOrdinal(BaseAddress, AddressOfNames, NumberOfNames, Name);
if (o < NumberOfNames)
{
o = AddressOfNameOrdinals[o];
}
}
if (o >= NumberOfFunctions)
{
return 0;
}
DWORD Rva = AddressOfFunctions[o];
if ((ULONG_PTR)Rva - (ULONG_PTR)exportRVA >= exportSize)
{
return RtlOffsetToPointer(ImageBase, Rva);
}
// forward export
PCSTR pfn = RtlOffsetToPointer(BaseAddress, Rva);
if (!(Name = strrchr(pfn, '.')))
{
return 0;
}
static const WCHAR DLL[] = L"DLL";
ULONG BytesInUnicodeString, BytesInMultiByteString = RtlPointerToOffset(pfn, ++Name);
if (0 > RtlMultiByteToUnicodeSize(&BytesInUnicodeString, pfn, BytesInMultiByteString) ||
(BytesInUnicodeString += sizeof(DLL)) >= MAXUSHORT )
{
return 0;
}
UNICODE_STRING DllName = {
0, (USHORT)BytesInUnicodeString, (PWSTR)alloca(BytesInUnicodeString)
};
if (0 > RtlMultiByteToUnicodeN(DllName.Buffer, DllName.MaximumLength,
&BytesInUnicodeString, pfn, BytesInMultiByteString))
{
return 0;
}
DllName.Length = (USHORT)BytesInUnicodeString;
if (0 > RtlAppendUnicodeToString(&DllName, DLL))
{
return 0;
}
static const UNICODE_STRING API_ = RTL_CONSTANT_STRING(L"API-");
static const UNICODE_STRING EXT_ = RTL_CONSTANT_STRING(L"EXT-");
if (!RtlPrefixUnicodeString(&API_, &DllName, TRUE) &&
!RtlPrefixUnicodeString(&EXT_, &DllName, TRUE))
{
return getWowProcs(&DllName, Name);
}
HMODULE hmod;
if (0 <= LdrLoadDll(0, 0, &DllName, &hmod))
{
ANSI_STRING as, *pas;
if (*Name == '#')
{
pas = 0;
o = strtoul(Name + 1, const_cast<char**>(&Name), 10);
if (*Name)
{
o = 0;
}
}
else
{
o = 0;
RtlInitAnsiString(pas = &as, Name);
}
PVOID pv, pvfn = 0;
if (0 <= LdrGetProcedureAddress(hmod, pas, o, &pv))
{
PLDR_DATA_TABLE_ENTRY ldte;
if (0 <= LdrFindEntryForAddress(pv, &ldte))
{
pvfn = getWowProcs(&ldte->BaseDllName, Name);
}
}
LdrUnloadDll(hmod);
return pvfn;
}
return 0;
}
PVOID getWowProcs(PCUNICODE_STRING DllName, PCSTR Name)
{
static const WCHAR KnownDlls32[] = L"\\KnownDlls32\\";
UNICODE_STRING ObjectName = {
0,
(USHORT)(sizeof(KnownDlls32) + DllName->Length),
(PWSTR)alloca(ObjectName.MaximumLength)
};
if (0 > RtlAppendUnicodeToString(&ObjectName, KnownDlls32) ||
0 > RtlAppendUnicodeStringToString(&ObjectName, DllName))
{
return 0;
}
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName, OBJ_CASE_INSENSITIVE };
HANDLE hSection;
PVOID pv = 0;
if (0 <= ZwOpenSection(&hSection, SECTION_QUERY|SECTION_MAP_READ, &oa))
{
SECTION_IMAGE_INFORMATION sii;
if (0 <= ZwQuerySection(hSection, SectionImageInformation, &sii, sizeof(sii), 0))
{
PVOID BaseAddress = 0;
SIZE_T ViewSize = 0;
if (0 <= ZwMapViewOfSection(hSection, NtCurrentProcess(), &BaseAddress, 0, 0, 0, &ViewSize, ViewUnmap, 0, PAGE_READONLY))
{
__try
{
if (PIMAGE_NT_HEADERS32 pinth = (PIMAGE_NT_HEADERS32)RtlImageNtHeader(BaseAddress))
{
pv = getWowProcs((PBYTE)sii.TransferAddress - pinth->OptionalHeader.AddressOfEntryPoint, BaseAddress, Name);
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}
ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
}
}
NtClose(hSection);
}
return pv;
}
PVOID GetCtrlRoutine(HANDLE hProcess)
{
BOOL bWow;
if (IsWow64Process(hProcess, &bWow))
{
static const UNICODE_STRING kernel32 = RTL_CONSTANT_STRING(L"kernel32.dll");
if (bWow)
{
static PVOID pvCtrlRoutine = 0;
if (!pvCtrlRoutine)
{
// GetOverlappedResultEx
// just for some extreme case, for better understand code in getWowProcs
// it not need here
// pvCtrlRoutine = getWowProcs(&kernel32, "GetOverlappedResultEx");
pvCtrlRoutine = getWowProcs(&kernel32, "CtrlRoutine");
}
return pvCtrlRoutine;
}
static PVOID pvCtrlRoutine = 0;
if (!pvCtrlRoutine)
{
if (HMODULE hmod = GetModuleHandle(kernel32.Buffer))
{
pvCtrlRoutine = GetProcAddress(hmod, "CtrlRoutine");
}
}
return pvCtrlRoutine;
}
return 0;
}
void SendCtrlEvent(HANDLE hProcess)
{
if (PVOID pv = GetCtrlRoutine(hProcess))
{
if (HANDLE hThread = CreateRemoteThread(hProcess, 0, 0, (PTHREAD_START_ROUTINE)pv, CTRL_C_EVENT, 0, 0))
{
CloseHandle(hThread);
return ;
}
}
TerminateProcess(hProcess, STATUS_CONTROL_C_EXIT);
}
void DemoUseCtrlC()
{
WCHAR AppName[MAX_PATH];
if (ExpandEnvironmentStringsW(L"%SystemRoot%\\syswow64\\ping.exe", AppName, _countof(AppName)))
{
STARTUPINFO si = { sizeof(si)};
PROCESS_INFORMATION pi;
if (CreateProcessW(AppName, const_cast<PWSTR>(L"* -n 999 8.8.8.8"), 0, 0, 0, 0, 0, 0, &si, &pi))
{
CloseHandle(pi.hThread);
MessageBoxW(HWND_DESKTOP, L"Break..", L"CTRL+C", MB_ICONINFORMATION);
SendCtrlEvent(pi.hProcess);
CloseHandle(pi.hProcess);
}
}
}