获取SSDT表函数名

本文介绍了一种在Windows环境下通过读取NTDLL文件并解析其导出表的方法。通过对NTDLL文件的读取和分析,可以获取到导出表中的函数名称及地址等关键信息。
ULONG GetSSDTName()
{
 KdBreakPoint();
 if (KeGetCurrentIrql() > PASSIVE_LEVEL)
 {
  return STATUS_UNSUCCESSFUL;
 }
 //设置NTDLL路径
 UNICODE_STRING uniFileName;
 RtlInitUnicodeString(&uniFileName, L"\\SystemRoot\\system32\\ntdll.dll");
 
 //初始化打开文件的属性
 OBJECT_ATTRIBUTES objectAttributes;
 InitializeObjectAttributes(&objectAttributes, &uniFileName,
        OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
  ////创建文件
 NTSTATUS Status;
 HANDLE FileHandle;
 Status = IoCreateFile(&FileHandle, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &objectAttributes,
       &ioStatus, 0, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT,
       NULL, 0, CreateFileTypeNone, NULL, IO_NO_PARAMETER_CHECKING);
 if (!NT_SUCCESS(Status))
 {
  DbgPrint("IoCreateFile failed!status:0x%08x\n", Status);
  return 0;
 }
  //获取文件信息
 IO_STATUS_BLOCK ioStatus;
 FILE_STANDARD_INFORMATION FileInformation;
 Status = ZwQueryInformationFile(FileHandle, &ioStatus, &FileInformation,
         sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation);
 if (!NT_SUCCESS(Status))
 {
  DbgPrint("ZwQueryInformationFile failed!status:0x%08x\n", Status);
  ZwClose( FileHandle );
  return 0;
 }
  //判断文件大小是否过大
 if (FileInformation.EndOfFile.HighPart != 0)
 {
  DbgPrint("File Size Too High");
  ZwClose(FileHandle);
  return 0;
 }
  //取文件大小
 ULONG uFileSize = FileInformation.EndOfFile.LowPart;
  //分配内存
 PVOID pBuffer = ExAllocatePoolWithTag(PagedPool, uFileSize, (ULONG)"NTDLL");
 if (pBuffer == NULL)
 {
  DbgPrint("ExAllocatePoolWithTag() == NULL");
  ZwClose(FileHandle);
  return 0;
 }
  //从头开始读取文件
 LARGE_INTEGER byteOffset;
 byteOffset.LowPart = 0;
 byteOffset.HighPart = 0;
 Status = ZwReadFile(FileHandle, NULL, NULL, NULL, &ioStatus, pBuffer, uFileSize, &byteOffset, NULL);
 if (!NT_SUCCESS(Status))
 {
  DbgPrint("ZwReadFile failed!status:0x%08x\n", Status);
  ZwClose(FileHandle);
  return 0;
 }
  //取出导出表
 PIMAGE_DOS_HEADER  pDosHeader;
 PIMAGE_NT_HEADERS  pNtHeaders;
 PIMAGE_SECTION_HEADER pSectionHeader;
 ULONG     FileOffset;
 PIMAGE_EXPORT_DIRECTORY pExportDirectory;
 //DLL内存数据转成DOS头结构
 pDosHeader = (PIMAGE_DOS_HEADER)pBuffer;
 //取出PE头结构
 pNtHeaders = (PIMAGE_NT_HEADERS)((ULONG)pBuffer + pDosHeader->e_lfanew);
 //判断PE头导出表表是否为空
 if (pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0)
 {
  DbgPrint("VirtualAddress == 0");
  return 0;
 }
 //取出导出表偏移
 FileOffset = pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
 
 //取出节头结构
 pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pNtHeaders + sizeof(IMAGE_NT_HEADERS));
 PIMAGE_SECTION_HEADER pOldSectionHeader = pSectionHeader;
 //遍历节结构进行地址运算
 for (WORD Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++)
 {
  if (pSectionHeader->VirtualAddress <= FileOffset &&
   FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData)
  {
   FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
  }
 }
 //导出表地址
 pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((ULONG)pBuffer + FileOffset);
  //取出导出表函数地址
 PULONG AddressOfFunctions;
 FileOffset = pExportDirectory->AddressOfFunctions;
 //遍历节结构进行地址运算
 pSectionHeader = pOldSectionHeader;
 for (WORD Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++)
 {
  if (pSectionHeader->VirtualAddress <= FileOffset &&
   FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData)
  {
   FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
  }
 }
  AddressOfFunctions = (PULONG)((ULONG)pBuffer + FileOffset);
 
 //取出导出表函数名字
 PUSHORT AddressOfNameOrdinals;
 FileOffset = pExportDirectory->AddressOfNameOrdinals;
 //遍历节结构进行地址运算
 pSectionHeader = pOldSectionHeader;
 for (WORD Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++)
 {
  if (pSectionHeader->VirtualAddress <= FileOffset &&
   FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData)
  {
   FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
  }
 }
 AddressOfNameOrdinals = (PUSHORT)((ULONG)pBuffer + FileOffset);
  //取出导出表函数序号
 PULONG AddressOfNames;
 FileOffset = pExportDirectory->AddressOfNames;
 //遍历节结构进行地址运算
 pSectionHeader = pOldSectionHeader;
 for (WORD Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++)
 {
  if (pSectionHeader->VirtualAddress <= FileOffset &&
   FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData)
  {
   FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
  }
 }
 AddressOfNames = (PULONG)((ULONG)pBuffer + FileOffset);
  //分析导出表
 ULONG uNameOffset;
 ULONG uOffset;
 LPSTR FunName;
 PVOID pFuncAddr;
 ULONG uServerIndex;
 ULONG uAddressOfNames;
 for (ULONG uIndex = 0; uIndex < pExportDirectory->NumberOfNames; uIndex++, AddressOfNames++, AddressOfNameOrdinals++)
 {
  uAddressOfNames = *AddressOfNames;
  pSectionHeader = pOldSectionHeader;
  for (WORD Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++)
  {
   if (pSectionHeader->VirtualAddress <= uAddressOfNames &&
    uAddressOfNames <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData)
   {
    uOffset = uAddressOfNames - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
   }
  } 
   FunName = (LPSTR)((ULONG)pBuffer + uOffset);
   if (FunName[0] == 'Z' && FunName[1] == 'w')
  {
   pSectionHeader = pOldSectionHeader;
   uOffset = (ULONG)AddressOfFunctions[*AddressOfNameOrdinals];
   for (WORD Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++)
   {
    if (pSectionHeader->VirtualAddress <=  uOffset&&
     uOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData)
    {
     uNameOffset = uOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
    }
   } 
   pFuncAddr = (PVOID)((ULONG)pBuffer + uNameOffset);
   uServerIndex = *(PULONG)((ULONG)pFuncAddr + 1);
   FunName[0] = 'N';
   FunName[1] = 't';
   KdPrint(("函数名为: %s 序列号为:%d\n", FunName, uServerIndex));
   
  }
 }
 ExFreePoolWithTag(pBuffer , (ULONG)"NTDLL");
 ZwClose(FileHandle);
 return 1;
}
 
本实例由VS2008开发,在提供了一套驱动开发框架的同时,又演示了如何获取Shadow SSDT函数原始地址的办法。 主要函数:ULONG GetShadowSSDT_Function_OriAddr(ULONG index); 原理说明: 根据特征码搜索导出函数KeAddSystemServiceTable来获取Shadow SSDT基址,以及通过ZwQuerySystemInformation()函数获取win32k.sys基址,然后解析PE定位到Shadow SSDT在win32k.sys的偏移地址,并通过进一步计算来得到Shadow SSDT函数的原始地址。 这里只测试了三个函数:(460)NtUserMessageCall、(475)NtUserPostMessage和(502)NtUserSendInput,具体使用时可以举一反三,网上完整的源代码实例并不太多,希望可以帮到真正有需要的朋友。 系统环境: 在WinXP SP3系统 + 瑞星杀毒软件 打印输出: [ LemonInfo : Loading Shadow SSDT Original Address Driver... ] [ LemonInfo : 创建“设备”值为:0 ] [ LemonInfo : 创建“设备”成功... ] [ LemonInfo : 创建“符号链接”状态值为:0 ] [ LemonInfo : 创建“符号链接”成功... ] [ LemonInfo : 驱动加载成功... ] [ LemonInfo : 派遣函数(DispatchRoutine) IRP 开始... ] [ LemonInfo : 派遣函数(DispatchRoutine) IRP Enter IRP_MJ_DEVICE_CONTROL... ] [ LemonInfo : 获取ShadowSSDT (460)NtUserMessageCall 函数的“当前地址”为:0xB83ECFC4,“起源地址”为:0xBF80EE6B ] [ LemonInfo : 获取ShadowSSDT (475)NtUserPostMessage 函数的“当前地址”为:0xB83ECFA3,“起源地址”为:0xBF8089B4 ] [ LemonInfo : 获取ShadowSSDT (502)NtUserSendInput 函数的“当前地址”为:0xBF8C31E7,“起源地址”为:0xBF8C31E7 ] [ LemonInfo : 派遣函数(DispatchRoutine) IRP_MJ_DEVICE_CONTROL 成功执行... ] [ LemonInfo : 派遣函数(DispatchRoutine) IRP 结束... ] [ LemonInfo : UnLoading Shadow SSDT Original Address Driver... ] [ LemonInfo : 删除“符号链接”成功... ] [ LemonInfo : 删除“设备”成功... ] [ LemonInfo : 驱动卸载成功... ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值