//KeServiceDescriptorTable -- 包含了 SSDT,(其 Shadow SSDT 处都是NULL)
//KeServiceDescriptorTableShadow -- 包含了 SSDT + Shadow SSDT,因此GUI线程不用转换也能调用 SSDT 中的函数
SYSTEM_SERVICE_TABLE *GetKeServiceDescriptorTableShadowAddress ()
{
//32位系统中导出了KeServiceDescriptorTable变量,
//其后紧接着有未导出的 KeServiceDescriptorTableShadow, 其前部分的内容和 KeServiceDescriptorTable 内容一样
//通过从 KeAddSystemServiceTable 导出函数开始搜索,并比较内存中的内容,从而找到 KeServiceDescriptorTableShadow,
//通过搜索 操作SSDT的函数实现中的有效内存地址的办法 来查找 Shadow SSDT
//TODO: 能取到 KeServiceDescriptorTable 导出变量的地址,返回 KeServiceDescriptorTable[1] 不就是 KeServiceDescriptorTableShadow 的地址了?
// First, obtain a pointer to KeAddSystemServiceTable
unsigned char *check = (unsigned char*)KeAddSystemServiceTable;
int i;
//Initialize an instance of System Service Table, will be used to
//obtain an address from KeAddSystemServiceTable
SYSTEM_SERVICE_TABLE *rc=0;
if (g_pSystemServiceTable)
{
return g_pSystemServiceTable;
}
// Make 100 attempts to match a valid address with that of KeServiceDescriptorTable
for (i=0; i<4096; i++) { //PAGE_SIZE
__try {
// try to obtain an address from KeAddSystemServiceTable
rc = *(SYSTEM_SERVICE_TABLE **)check;
// if this address is NOT valid OR it itself is the address of
//KeServiceDescriptorTable OR its first entry is NOT equal
//to the first entry of KeServiceDescriptorTable
if (!MmIsAddressValid (rc)
|| (rc == (SYSTEM_SERVICE_TABLE *)KeServiceDescriptorTable)
|| (memcmp (rc, KeServiceDescriptorTable, sizeof (*rc)) != 0)) {
// Proceed with the next address
check++;
// don't forget to reset the old address
rc = 0;
}
} __except (EXCEPTION_EXECUTE_HANDLER)
{
rc = 0;
}
// when the loop is completed, check if it produced a valid address
if (rc)
{
// because if it didn't, we failed to find the address of KeServiceDescriptorTableShadow
break;
}
}
// otherwise, there is a valid address! So return it!
g_pSystemServiceTable = rc;
if (g_pSystemServiceTable)
{
KdPrint(("g_pSystemServiceTable : %#x, SSDT EntryCount=%d, Shadow EntryCount=%d\n",
g_pSystemServiceTable, g_pSystemServiceTable[0].NumberOfServices,
g_pSystemServiceTable[1].NumberOfServices));
}
else
{
KdPrint(("!!! Find Shadow SSDT Failed from KeAddSystemServiceTable=%p\n", KeAddSystemServiceTable));
}
return g_pSystemServiceTable;
}
VOID
OsrUsbIoctlGetInterruptMessage(
_In_ WDFDEVICE Device,
_In_ NTSTATUS ReaderStatus
)
/*++
Routine Description
This method handles the completion of the pended request for the IOCTL
IOCTL_OSRUSBFX2_GET_INTERRUPT_MESSAGE.
Arguments:
Device - Handle to a framework device.
Return Value:
None.
--*/
{
NTSTATUS status;
WDFREQUEST request;
PDEVICE_CONTEXT pDevContext;
size_t bytesReturned = 0;
PSWITCH_STATE switchState = NULL;
pDevContext = GetDeviceContext(Device);
do {
//
// Check if there are any pending requests in the Interrupt Message Queue.
// If a request is found then complete the pending request.
//
status = WdfIoQueueRetrieveNextRequest(pDevContext->InterruptMsgQueue, &request);
if (NT_SUCCESS(status)) {
status = WdfRequestRetrieveOutputBuffer(request,
sizeof(SWITCH_STATE),
&switchState,
NULL);// BufferLength
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL,
"User's output buffer is too small for this IOCTL, expecting a SWITCH_STATE\n");
bytesReturned = sizeof(SWITCH_STATE);
} else {
//
// Copy the state information saved by the continuous reader.
//
if (NT_SUCCESS(ReaderStatus)) {
switchState->SwitchesAsUChar = pDevContext->CurrentSwitchState;
bytesReturned = sizeof(SWITCH_STATE);
} else {
bytesReturned = 0;
}
}
//
// Complete the request. If we failed to get the output buffer then
// complete with that status. Otherwise complete with the status from the reader.
//
WdfRequestCompleteWithInformation(request,
NT_SUCCESS(status) ? ReaderStatus : status,
bytesReturned);
status = STATUS_SUCCESS;
} else if (status != STATUS_NO_MORE_ENTRIES) {
KdPrint(("WdfIoQueueRetrieveNextRequest status %08x\n", status));
}
request = NULL;
} while (status == STATUS_SUCCESS);
return;
}
BOOLEAN
SepRmCommandServerThreadInit(
VOID
)
/*++
Routine Description:
This function performs initialization of the Reference Monitor Server
thread. The following steps are performed.
o Wait on the LSA signaling the event. When the event is signaled,
the LSA has already created the LSA Command Server LPC Port
o Close the LSA Init Event Handle. The event is not used again.
o Listen for the LSA to connect to the Port
o Accept the connection.
o Connect to the LSA Command Server LPC Port
Arguments:
None.
Return Value:
--*/
{
NTSTATUS Status;
UNICODE_STRING LsaCommandPortName;
PORT_MESSAGE ConnectionRequest;
SECURITY_QUALITY_OF_SERVICE DynamicQos;
OBJECT_ATTRIBUTES ObjectAttributes;
PORT_VIEW ClientView;
REMOTE_PORT_VIEW LsaClientView;
BOOLEAN BooleanStatus = TRUE;
PAGED_CODE();
//
// Save a pointer to our process so we can get back into this process
// to send commands to the LSA (using a handle to an LPC port created
// below).
//
SepRmLsaCallProcess = PsGetCurrentProcess();
ObReferenceObject(SepRmLsaCallProcess);
//
// Wait on the LSA signaling the event. This means that the LSA
// has created its command port, not that LSA initialization is
// complete.
//
Status = ZwWaitForSingleObject(
SepRmState.LsaInitEventHandle,
FALSE,
NULL);
if ( !NT_SUCCESS(Status) ) {
KdPrint(("Security Rm Init: Waiting for LSA Init Event failed 0x%lx\n", Status));
goto RmCommandServerThreadInitError;
}
//
// Close the LSA Init Event Handle. The event is not used again.
//
ZwClose(SepRmState.LsaInitEventHandle);
//
// Listen for a connection to be made by the LSA to the Reference Monitor
// Command Port. This connection will be made by the LSA process.
//
ConnectionRequest.u1.s1.TotalLength = sizeof(ConnectionRequest);
ConnectionRequest.u1.s1.DataLength = (CSHORT)0;
Status = ZwListenPort(
SepRmState.RmCommandServerPortHandle,
&ConnectionRequest
);
if (!NT_SUCCESS(Status)) {
KdPrint(("Security Rm Init: Listen to Command Port failed 0x%lx\n",
Status));
goto RmCommandServerThreadInitError;
}
//
// Obtain a handle to the LSA process for use when auditing.
//
InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, NULL );
Status = ZwOpenProcess(
&SepLsaHandle,
PROCESS_VM_OPERATION | PROCESS_VM_WRITE,
//.........这里部分代码省略.........
//.........这里部分代码省略.........
);
ReplyMessage.ReturnedStatus = STATUS_SUCCESS;
} else {
Status = ZwRequestWaitReplyPort(
SepRmState.LsaCommandPortHandle,
(PPORT_MESSAGE) &CommandMessage,
(PPORT_MESSAGE) &ReplyMessage
);
}
//
// If the command was successful, copy the data back to the output
// buffer.
//
if (NT_SUCCESS(Status)) {
//
// Move output from command (if any) to buffer. Note that this
// is done even if the command returns status, because some status
// values are not errors.
//
if (ARGUMENT_PRESENT(WorkQueueItem->ReplyBuffer)) {
RtlCopyMemory(
WorkQueueItem->ReplyBuffer,
ReplyMessage.ReplyBuffer,
WorkQueueItem->ReplyBufferLength
);
}
//
// Return status from command.
//
Status = ReplyMessage.ReturnedStatus;
if (!NT_SUCCESS(Status)) {
KdPrint(("Security: Command sent from RM to LSA returned 0x%lx\n",
Status));
}
} else {
KdPrint(("Security: Sending Command RM to LSA failed 0x%lx\n", Status));
}
//
// On return from the LPC call to the LSA, we expect the called
// LSA worker routine to have copied the Command Parameters
// buffer (if any). If a custom shared memory buffer was allocated,
// free it now.
//
if (CommandMessage.CommandParamsMemoryType == SepRmLsaCustomSharedMemory) {
RegionSize = 0;
Status = ZwFreeVirtualMemory(
SepLsaHandle,
(PVOID *) &CommandMessage.CommandParams,
&RegionSize,
MEM_RELEASE
);
ASSERT(NT_SUCCESS(Status));
}
}
//
// Clean up. We must call the cleanup functions on its parameter
// and then free the used WorkQueueItem itself.
//
if ( ARGUMENT_PRESENT( WorkQueueItem->CleanupFunction)) {
(WorkQueueItem->CleanupFunction)(WorkQueueItem->CleanupParameter);
}
//
// Determine if there is more work to do on this list
//
WorkQueueItem = SepDequeueWorkItem();
}
KeDetachProcess();
if ( LocalListLength > SepLsaQueueLength ) {
SepLsaQueueLength = LocalListLength;
}
return Status;
}
BOOLEAN
SeRmInitPhase1(
)
/*++
Routine Description:
This function is called by Phase 1 System Initialization to initialize
the Security Reference Monitor. Note that initialization of the
Reference Monitor Global State has already been performed in Phase 0
initialization to allow access validation routines to operate without
having to check that Reference Monitor Initialization is complete.
The steps listed below are performed in this routine. The remainder
of Reference Monitor initialization requires the LSA subsystem to have run,
so that initialization is performed in a separate thread (the RM Command
Server Thread, see below), so that the present thread can create the
Session Manager which execs the LSA.
o Create the Reference Monitor Command LPC port. The LSA subsystem sends
commands (e.g. turn on auditing) which change the Reference Monitor
Global State.
o Create an Event for use in synchronizing with the LSA subsystem. The
LSA will signal the event when the portion of LSA initialization upon
with the Reference Monitor depends is complete. The Reference Monitor
uses another LPC port, called the LSA Command Port to send commands
to the LSA, so the RM must know that this port has been created before
trying to connect to it.
o Create the Reference Monitor Command Server Thread. This thread is
a permanent thread of the System Init process that fields the Reference
Monitor State Change commands described above.
Arguments:
None.
Return Value:
BOOLEAN - TRUE if Rm Initialization (Phase 1) succeeded, else FALSE
--*/
{
NTSTATUS Status;
UNICODE_STRING RmCommandPortName;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING LsaInitEventName;
OBJECT_ATTRIBUTES LsaInitEventObjectAttributes;
SECURITY_DESCRIPTOR LsaInitEventSecurityDescriptor;
ULONG AclSize;
PAGED_CODE();
//
// Create an LPC port called the Reference Monitor Command Port.
// This will be used by the LSA to send commands to the Reference
// Monitor to update its state data.
//
RtlInitUnicodeString( &RmCommandPortName, L"\\SeRmCommandPort" );
InitializeObjectAttributes(
&ObjectAttributes,
&RmCommandPortName,
0,
NULL,
NULL
);
Status = ZwCreatePort(
&SepRmState.RmCommandServerPortHandle,
&ObjectAttributes,
sizeof(SEP_RM_CONNECT_INFO),
sizeof(RM_COMMAND_MESSAGE),
sizeof(RM_COMMAND_MESSAGE) * 32
);
if( !NT_SUCCESS(Status) ) {
KdPrint(("Security: Rm Create Command Port failed 0x%lx\n", Status));
return FALSE;
}
//
// Prepare to create an event for synchronizing with the LSA.
// First, build the Security Descriptor for the Init Event Object
//
Status = RtlCreateSecurityDescriptor(
&LsaInitEventSecurityDescriptor,
SECURITY_DESCRIPTOR_REVISION
);
if (!NT_SUCCESS(Status)) {
KdPrint(("Security: Creating Lsa Init Event Desc failed 0x%lx\n",
Status));
return FALSE;
//.........这里部分代码省略.........
VOID
SepRmCommandServerThread(
IN PVOID StartContext
)
/*++
Routine Description:
This function is executed indefinitely by a dedicated permanent thread
of the Sysinit Process, called the Reference Monitor Server Thread.
This thread updates Reference Monitor Global State Data by dispatching
commands sent from the LSA through the the Reference Monitor LPC Command
Port. The following steps are repeated indefinitely:
o Initialize RM Command receive and reply buffer headers
o Perform remaining Reference Monitor initialization involving LSA
o Wait for RM command sent from LSA, send reply to previous command
(if any)
o Validate command
o Dispatch to command worker routine to execute command.
Arguments:
None.
Return Value:
None.
--*/
{
NTSTATUS Status;
PRM_REPLY_MESSAGE Reply;
RM_COMMAND_MESSAGE CommandMessage;
RM_REPLY_MESSAGE ReplyMessage;
PAGED_CODE();
//
// Perform the rest of the Reference Monitor initialization, involving
// synchronization with the LSA or dependency on the LSA having run.
//
if (!SepRmCommandServerThreadInit()) {
KdPrint(("Security: Terminating Rm Command Server Thread\n"));
return;
}
Status = PoRequestShutdownEvent (NULL);
if (!NT_SUCCESS (Status)) {
ZwClose (SepRmState.RmCommandPortHandle);
ZwClose (SepRmState.RmCommandServerPortHandle);
ZwClose (SepRmState.LsaCommandPortHandle);
ZwClose (SepLsaHandle);
SepRmState.RmCommandPortHandle = NULL;
SepRmState.RmCommandServerPortHandle = NULL;
SepRmState.LsaCommandPortHandle = NULL;
SepLsaHandle = NULL;
return;
}
//
// Initialize LPC port message header type and length fields for the
// received command message.
//
CommandMessage.MessageHeader.u2.ZeroInit = 0;
CommandMessage.MessageHeader.u1.s1.TotalLength =
(CSHORT) sizeof(RM_COMMAND_MESSAGE);
CommandMessage.MessageHeader.u1.s1.DataLength =
CommandMessage.MessageHeader.u1.s1.TotalLength -
(CSHORT) sizeof(PORT_MESSAGE);
//
// Initialize the LPC port message header type and data sizes for
// for the reply message.
//
ReplyMessage.MessageHeader.u2.ZeroInit = 0;
ReplyMessage.MessageHeader.u1.s1.TotalLength =
(CSHORT) sizeof(RM_COMMAND_MESSAGE);
ReplyMessage.MessageHeader.u1.s1.DataLength =
ReplyMessage.MessageHeader.u1.s1.TotalLength -
(CSHORT) sizeof(PORT_MESSAGE);
//
// First time through, there is no reply.
//
Reply = NULL;
//
// Now loop indefinitely, processing incoming Rm commands from the LSA.
//
for(;;) {
//.........这里部分代码省略.........
请发表评论