首页
社区
课程
招聘
[原创]精确区分windows内核Image文件
发表于: 2009-9-18 12:26 20148

[原创]精确区分windows内核Image文件

2009-9-18 12:26
20148

在阅读本文前,先确定几个概念。
FileName(文件名):即文件在硬盘上的名字,这个名字随着复制、改名,会变化。
InternalName(文件原始名):在附加了.rc编译时,编译器会将.rc中的名字链接进去,这个名字,不会随着文件的复制改名而变化(用PE工具修改后再改CheckSum除外), 在Windows中,右击文件名->属性->版本->内部名称 就是此字段
FileVersion(版本) 在附加了.rc编译时,编译器会将.rc中的版本链接进去,在Windows中,右击文件名->属性->版本->文件版本 就是此字段
nt内核:即ntoskrnl.exe/ ntkrnlmp.exe/ ntkrnlpa.exe/ ntkrpamp.exe
win32k内核:即win32k.sys

我们在做windows内核相关工作的时候,经常需要确定当前是哪个内核。

就XP SP3来说,nt内核就有以下几种历史版本
5.1.2600.5512 (xpsp.080413-2111)
5.1.2600.5657 (xpsp_sp3_gdr.080814-1236)
5.1.2600.5755 (xpsp_sp3_gdr.090206-1234)
5.1.2600.5781 (xpsp_sp3_gdr.090321-1317)
除了gdr版本外,还有qfe版本。

win32k内核
5.1.2600.5796 (xpsp_sp3_gdr.090417-1242)
win32k.sys不管系统是PAE还是非PAE,单CPU还是多CPU,都是win32k.sys,这点不会给我们造成困扰。但是ntkrnl就不一样了。对于上面说的nt内核的每1个历史版本,它都有4种版本,分别是
        PAE        多CPU
ntoskrnl.exe        N        N
ntkrnlmp.exe        N        Y
ntkrnlpa.exe        Y        N
ntkrpamp.exe        Y        Y
(假如你在windows\system32下没找到这4个文件的话,你可以到C:\WINDOWS\Driver Cache\i386里去找sp2.cab或sp3.cab,然后
expand sp2.cab -F:ntoskrnl.exe c:\windows\system32)

当系统从单CPU升级为多CPU时,windows会“提示找到新硬件”,用多核的nt内核文件覆盖当前单核nt内核文件,并重新启动,启动后就用上多CPU的nt内核,但当从多CPU降为单CPU时,仍然在使用多CPU的nt内核,所以,我们不能根据系统中当前有几个CPU来判断当前是用哪个内核文件。但是可以用当前是否为PAE模式来判断当前内核文件。

如果你的目的只是想Load硬盘上的内核文件到内存中,进行PE解析(例如找到原始SSDT/ShadowSSDT/IDT及其他ring0 inline hook check),那么你可以看方式1,如果你要精确识别,请看方式2
方式1:
使用PsLoadedModuleList或NtQuerySystemInformation或ZWQuerySystemInformation,找到nt内核和win32k内核,根据PLDR_DATA_TABLE_ENTRY->FullDllName这个FileName,载入硬盘上相应文件,展开,修正IAT,修正Fixup(载入/展开等就不列在这里了),ARK工具就可以进行hook识别了。
(感谢qihoocom 轩辕小聪的提醒,我才发现,原来PLDR_DATA_TABLE_ENTRY->FullDllName是准确的硬盘PE Image文件名字)

方式2:
如果你想借助.pdb来找出内核未导出函数的名字、地址等,那你就一定要精确地区分出系统中是哪个内核(InternalName)和具体版本(FileVersion)。
而PsLoadedModuleList或NtQuerySystemInformation或ZWQuerySystemInformation中得到的nt内核FileName,与该文件所对应的.pdb,并不存在直接关联(因为Copy的原因,FileName!= InternalName)
那么,怎么办呢?如何用程序实现像Windows看软件版本一样的功能呢?
经实践,发现可以对ntkernel的BaseAddr,用PE解析的方法,找到Resource->Version,取InternalName和FileVersion,见下表
CompanyName        Microsoft Corporation
InternalName        ntkrnlpa.exe
FileVersion        5.1.2600.5755 (xpsp_sp3_gdr.090206-1234)
ProductVersion        5.1.2600.5755
对于同样的5.1.2600 build 5755的windowsXP而言,存在2个完全不同的版本:gdr(General Distribution Releases)和qfe(Quick Fix Engineering),所以ProductVersion不能作为内核的区别。
InternalName和FileVersion,这2个字符串能唯一地确定内核Image版本。
代码范例(以下代码在VC++/DDK都可编译):
DWORD VA2RAW(DWORD va,PVA2RAW_REC pva2raw_rec)
{
        int i;
        for (i=0;pva2raw_rec[i].VA!=0;i++)
                if ((va>=pva2raw_rec[i].VA)&&(va<=pva2raw_rec[i].VAEnd))
                        return pva2raw_rec[i].RAW+va-pva2raw_rec[i].VA;
        return 0;
}
int PE_Head_Pointer(char *pbuf,
                PIMAGE_DOS_HEADER        *pIDH,
                PIMAGE_NT_HEADERS        *pINH,
                PIMAGE_FILE_HEADER        *pIFH,
                PIMAGE_OPTIONAL_HEADER        *pIOH,
                PIMAGE_SECTION_HEADER        *pISH)
{
        int i;

        *pIDH=(PIMAGE_DOS_HEADER)pbuf;

        if  ((*pIDH)->e_magic!=IMAGE_DOS_SIGNATURE)
                return -1;
        *pINH=(PIMAGE_NT_HEADERS)((DWORD)pbuf+(*pIDH)->e_lfanew);

        if ((*pINH)->Signature!=IMAGE_NT_SIGNATURE)
                return -1;
        *pIFH=&((*pINH)->FileHeader);

        *pIOH=&((*pINH)->OptionalHeader);
        if ((*pIOH)->Magic!=IMAGE_NT_OPTIONAL_HDR32_MAGIC)
                return -1;
        #ifdef COMPILE_DEBUG1
                DbgPrint("IMAGE_DIRECTORY_ENTRIES\n");
                for (i=0;i<IMAGE_NUMBEROF_DIRECTORY_ENTRIES;i++)
                {
                        switch(i)
                        {
                        case IMAGE_DIRECTORY_ENTRY_EXPORT:
                                        DbgPrint("0 Export Directory");
                                break;
                        case IMAGE_DIRECTORY_ENTRY_IMPORT:
                                        DbgPrint("1 Import Directory");
                                break;
                        case IMAGE_DIRECTORY_ENTRY_RESOURCE:
                                        DbgPrint("2 Resource Directory");
                                break;
                        case IMAGE_DIRECTORY_ENTRY_EXCEPTION:
                                        DbgPrint("3 Exception Directory");
                                break;
                        case IMAGE_DIRECTORY_ENTRY_SECURITY:
                                        DbgPrint("4 Security Directory");
                                break;
                        case IMAGE_DIRECTORY_ENTRY_BASERELOC:
                                        DbgPrint("5 Base Relocation Table");
                                break;
                        case IMAGE_DIRECTORY_ENTRY_DEBUG:
                                        DbgPrint("6 Debug Directory");
                                break;
                        case IMAGE_DIRECTORY_ENTRY_ARCHITECTURE:
                                        DbgPrint("7 Architecture Specific Data");
                                break;
                        case IMAGE_DIRECTORY_ENTRY_GLOBALPTR:
                                        DbgPrint("8 RVA of GP");
                                break;
                        case IMAGE_DIRECTORY_ENTRY_TLS:
                                        DbgPrint("9 TLS Directory");
                                break;
                        case IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG:
                                        DbgPrint("10 Load Configuration Directory");
                                break;
                        case IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT:
                                        DbgPrint("11 Bound Import Directory in headers");
                                break;
                        case IMAGE_DIRECTORY_ENTRY_IAT:
                                        DbgPrint("12 Import Address Table");
                                break;
                        case IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT:
                                        DbgPrint("13 Delay Load Import Descriptors");
                                break;
                        case IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR:
                                        DbgPrint("14 COM Runtime descriptor");
                                break;
                        default:
                                        DbgPrint("%d",i);
                        }
                        DbgPrint("        %0.8X %0.8X\n",
                                (*pIOH)->DataDirectory[i].VirtualAddress,
                                (*pIOH)->DataDirectory[i].Size
                                );       
                }
                DbgPrint("ImageBase=0x%0.8X\tBaseofCode=0x%0.8X\tIDAStart(CodeStart)=0x%0.8X\n",
                        (*pIOH)->ImageBase,(*pIOH)->BaseOfCode,(*pIOH)->ImageBase+(*pIOH)->BaseOfCode);
        #endif
        *pISH=(PIMAGE_SECTION_HEADER)((*pIOH)+1);

        return 1;
}

int PE_Dump_Resource_Dir(char *pbuf,BYTE bRAWFile,
                                DWORD        dwFirstIRD_Offset,
                                PIMAGE_RESOURCE_DIRECTORY pIRD,
                                BYTE        bLevel,
                                DWORD        dwResourceType,
                                char *szCompanyName,
                                int iCompanyNameLen,
                                char *szFileVersion,
                                int iFileVersionLen,
                                PVA2RAW_REC        pva2raw_rec)
{
        int i,j,iLen=0;
        DWORD dwAddr;
        char Buf[1024],
                szTemp1[256]={0};
        PIMAGE_RESOURCE_DIRECTORY_ENTRY        pIRDE;
        PIMAGE_RESOURCE_DATA_ENTRY                pIRDataE;
        PIMAGE_RESOURCE_DIR_STRING_U        pRDSU;

        if (bLevel>=4)        //4层及以上结构,就不再挖了
                return 1;

        pIRDE=(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pIRD +1);
        for (i=0;i <pIRD->NumberOfIdEntries+pIRD->NumberOfNamedEntries;i++,pIRDE++)
        {
                if (pIRDE->DataIsDirectory)
                {
                        #if (!defined _WINDOWS) && (!defined _CONSOLE)
                                DbgPrint("DataIsDirectory pbuf+%X=%X\n",
                                        pIRDE->OffsetToDirectory+(DWORD)pIRD-(DWORD)pbuf,
                                        pIRDE->OffsetToDirectory+(DWORD)pIRD);
                        #endif
                        PE_Dump_Resource_Dir(pbuf,bRAWFile,dwFirstIRD_Offset,
                                (PIMAGE_RESOURCE_DIRECTORY)(pIRDE->OffsetToDirectory+(DWORD)pbuf+dwFirstIRD_Offset),bLevel+1,
                                dwResourceType,szCompanyName,iCompanyNameLen,szFileVersion,iFileVersionLen,
                                pva2raw_rec);
                }else
                {
                        pIRDataE = (PIMAGE_RESOURCE_DATA_ENTRY)
                                                        ((DWORD)pbuf+dwFirstIRD_Offset + pIRDE->OffsetToData);
                        #if (!defined _WINDOWS) && (!defined _CONSOLE)
                                DbgPrint("DataRVA: OffsetToData=%05X        Size=%X        CodePage=%X\n",
                                                pIRDataE->OffsetToData,
                                                pIRDataE->Size,        pIRDataE->CodePage);
                        #endif
                        if (dwResourceType==RESOURCE_ID_VERSION)
                        {
                                if (bRAWFile)
                                        dwAddr=VA2RAW(pIRDataE->OffsetToData,pva2raw_rec);
                                else
                                        dwAddr=pIRDataE->OffsetToData;
                                pRDSU=(PIMAGE_RESOURCE_DIR_STRING_U)((DWORD)pbuf+dwAddr);

                                #if (defined _WINDOWS) || (defined _CONSOLE)
                                        iLen=WideCharToMultiByte(CP_ACP, 0, pRDSU->NameString, pRDSU->Length,Buf, sizeof(Buf), 0, 0);
                                        iLen=iLen/2;
                                #else
                                        //iLen=Clh_Unicode2Char((UNICODE_STRING *)pRDSU,Buf,sizeof(Buf));
                                        iLen=Clh_WChar2Char(pRDSU->NameString,pRDSU->Length,Buf,sizeof(Buf));
                                #endif
                               
                                for (j=0;j<iLen-1;j++)
                                {
                                        if (szCompanyName)
                                                if (strcmp("CompanyName",&Buf[j])==0)
                                                        strcpy(szCompanyName,&Buf[j+strlen("CompanyName")+2]);
                                        if (szFileVersion)
                                        {
                                                if (strcmp("InternalName",&Buf[j])==0)
                                                        strcpy(szFileVersion,&Buf[j+strlen("InternalName")+1]);
                                                if (strcmp("FileVersion",&Buf[j])==0)
                                                        strcpy(szTemp1,&Buf[j+strlen("FileVersion")+2]);
                                        }
                                }
                                szFileVersion[strlen(szFileVersion)-4]='_';
                                szFileVersion[strlen(szFileVersion)-3]=0;
                                strcat(szFileVersion,szTemp1);
                                #if (!defined _WINDOWS) && (!defined _CONSOLE)
                                        if (szCompanyName)
                                                DbgPrint("szCompanyName=%s\n",szCompanyName);
                                        if (szFileVersion)
                                                DbgPrint("szFileVersion=%s\n",szFileVersion);
                                #endif
                        }
                }//End of if (pIRDE->DataIsDirectory)
        }//End of for (i=0;i <pIRD->

        return 1;
}

int PE_Dump_Resource(char *pbuf,BYTE bRAWFile,
                DWORD dwFirstIRD_Offset,
                char *szCompanyName,
                int iCompanyNameLen,
                char *szFileVersion,
                int iFileVersionLen,
                PVA2RAW_REC        pva2raw_rec)
{
        DWORD dwDiff,dwAddr;
        int i,iCount,iFixupCount;
        PIMAGE_RESOURCE_DIRECTORY                pIRD;
        PIMAGE_RESOURCE_DIRECTORY_ENTRY        pIRDE;
        PIMAGE_RESOURCE_DIR_STRING_U prdsu;

        pIRD=(PIMAGE_RESOURCE_DIRECTORY)((DWORD)pbuf + dwFirstIRD_Offset);
        pIRDE=(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pIRD +1);
        //DbgPrint("pIRD=%X        pIRDE=%X\n",pIRD,pIRDE);
        for (i=0;i <pIRD->NumberOfIdEntries+pIRD->NumberOfNamedEntries;i++,pIRDE++)
        {
                if (pIRDE->Id==RESOURCE_ID_VERSION)
                {
                        //printf("RESOURCE_ID_VERSION\n");
                        if (pIRDE->DataIsDirectory)
                        {
                                #if (!defined _WINDOWS) && (!defined _CONSOLE)
                                        DbgPrint("DataIsDirectory Addr=%X\n",pIRDE->OffsetToDirectory+(DWORD)pIRD);
                                #endif
                                PE_Dump_Resource_Dir(pbuf,bRAWFile,
                                        dwFirstIRD_Offset,(PIMAGE_RESOURCE_DIRECTORY)(pIRDE->OffsetToDirectory+(DWORD)pIRD),1,RESOURCE_ID_VERSION,
                                        szCompanyName,iCompanyNameLen,szFileVersion,iFileVersionLen,
                                        pva2raw_rec);
                        }
                }
        }
        return 1;
}

int PE_Get_VersionString(char *pbuf,BYTE bRAWFile,
                char *szCompanyName,
                int iCompanyNameLen,
                char *szFileVersion,
                int iFileVersionLen)
{
        int i;
        DWORD dwAddr;
        PIMAGE_DOS_HEADER        pIDH;
        PIMAGE_NT_HEADERS        pINH;
        PIMAGE_FILE_HEADER        pIFH;
        PIMAGE_OPTIONAL_HEADER        pIOH;
        PIMAGE_SECTION_HEADER        pISH;
        VA2RAW_REC        PE_va2raw_rec[50];

        #ifdef COMPILE_DEBUG1
                DbgPrint("PE_Get_VersionString(%X)\n",pbuf);
        #endif

        if (PE_Head_Pointer(pbuf,&pIDH,&pINH,&pIFH,&pIOH,&pISH)<0)
                return -1;
        if (bRAWFile)
        {
                memset(&PE_va2raw_rec,0,sizeof(VA2RAW_REC));
                //1:生成反向查找表
                for (i=0;i<pIFH->NumberOfSections;i++)
                {
                        PE_va2raw_rec[i].VA                =(pISH+i)->VirtualAddress;
                        PE_va2raw_rec[i].VAEnd        =(pISH+i)->VirtualAddress        +(pISH+i)->Misc.VirtualSize;
                        PE_va2raw_rec[i].RAW        =(pISH+i)->PointerToRawData;
                        PE_va2raw_rec[i].RAWEnd        =(pISH+i)->PointerToRawData        +(pISH+i)->Misc.VirtualSize;
                }
        }
        if (pIOH->DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size>0)
        {
                if (bRAWFile)
                        dwAddr=VA2RAW(pIOH->DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress,PE_va2raw_rec);
                else
                        dwAddr=pIOH->DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
                if (PE_Dump_Resource(pbuf,bRAWFile,dwAddr,
                        szCompanyName,iCompanyNameLen,szFileVersion,iFileVersionLen,
                        PE_va2raw_rec)<0 )
                {
                        #if (!defined _WINDOWS) && (!defined _CONSOLE)
                                DbgPrint("PE_Dump_Resource error\n");
                        #endif
                }
        }
        return 1;
}

当然,MSDN也介绍了API GetVersionInfo,但是笔者认为,既然是ARK,那么对于并不复杂的API,能自己实现的,还是自己实现为好。以下是GetVersionInfo的范例
   void GetInternalName()
   {
      // Get the file version for the notepad.
      FileVersionInfo^ myFileVersionInfo = FileVersionInfo::GetVersionInfo( "%systemroot%\\Notepad.exe" );
      
      // Print the internal name.
      textBox1->Text = String::Concat( "Internal name: ", myFileVersionInfo->InternalName );
   }


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 7
支持
分享
最新回复 (25)
雪    币: 522
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
沙发
ZWQUERYXXX  不是就很精确了嘛   你这样反而不精确了.

难道用ZWQUERYXXX取内核名字   也怕被HOOK呀
2009-9-18 15:05
0
雪    币: 722
活跃值: (123)
能力值: ( LV12,RANK:300 )
在线值:
发帖
回帖
粉丝
3
“根据PsLoadedModuleList或NtQuerySystemInformation找到的ntkrnlpa.exe也不是真正的内核PE文件在硬盘的名字”
这句话似乎有误,实际上ntkrnlpa.exe就是在硬盘上的名字,但是在双核的情况下,这不是其源文件名(或内部名称),其源文件名或内部名称(及.pdb的名字)为ntkrpamp.exe。
楼主后面的做法,也就是取版本信息中的内部名称而已。
2009-9-18 16:05
0
雪    币: 635
活跃值: (101)
能力值: ( LV12,RANK:420 )
在线值:
发帖
回帖
粉丝
4
源文件名其实最终可以从symbol信息里去,这个是最准确的
2009-9-18 16:17
0
雪    币: 258
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
谢谢提醒。呵呵
2009-9-18 17:19
0
雪    币: 258
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
6
以上是偶的ARK工具中的部分代码。所以直接分析,避开常用方式居多。
2009-9-18 17:20
0
雪    币: 258
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
7
大牛,麻烦多提示一些
2009-9-18 17:22
0
雪    币: 722
活跃值: (123)
能力值: ( LV12,RANK:300 )
在线值:
发帖
回帖
粉丝
8
举例来说,假如你用PsLoadedModuleList或NtQuerySystemInformation找到内核名字为“ntoskrnl.exe”,而真正的内核在硬盘上的文件可能是“ntoskrnl.exe”或“ntkrnlmp.exe”
假如你用PsLoadedModuleList或NtQuerySystemInformation找到内核名字为“ntkrnlpa.exe”,而真正的内核在硬盘上的文件可能是“ntkrnlpa.exe”或“ntkrpamp.exe”
而到底是“ntoskrnl.exe”还是“ntkrnlmp.exe”,在内存RAW解析的时候并不重要

难道“将磁盘中的内核文件自己映像到内存中,来与内核中的比对以还原可能的HOOK”中的“磁盘中的内核文件”,不是指像C:\WINDOWS\system32\ntoskrnl.exe这样的文件路径?

当系统从单CPU升级为多CPU时,windows会“提示找到新硬件”并重新启动,启动后就用上多CPU的内核,但当从多CPU降为单CPU时,仍然在使用多CPU的内核

这个原因到底是为什么,似乎应该是当系统启动时检测到在多CPU的情况下内核文件ntoskrnl.exe或ntkrnlpa.exe却还是单核版本的时候,会把相应的多核版本的文件拷贝覆盖单核版本的文件。这样ntoskrnl.exe或ntkrnlpa.exe(磁盘上的文件)实际上是多核版本文件的拷贝。之后这两个文件被加载。这样双核再变单核的时候,这两个文件已经是多核版本了,所以“回不来”了。

因此,就“磁盘上的文件”而言,被加载的文件名仍然是ntoskrnl.exe或ntkrnlpa.exe,只不过它们其实是多核版本的拷贝,因此它们的源文件名和它们对应的pdb名都是多核版本的名字。你后面的取其版本信息中的内部文件名的做法,难道不是基于此吗?

磁盘中的文件名,和它真正对应的pdb名不一样,所以楼主要解析pdb的时候才需要搞到其源文件名(或者反过来说,解析其符号信息也可以得到其源文件名),难道不是这样吗?

1. 程序把1.exe拷贝成2.exe,然后加载2.exe
2. 程序在某些环境下不加载2.exe而直接加载1.exe
以上两个过程虽然结果看起来一样,但是是不一样的过程,实际上应该是第一个过程吧?因此PsLoadedModuleList和NtQuerySystemInformation并没有错,因为它们只是告诉我们“现在加载的文件是哪一个文件”,而不是告诉我们“这个文件原来是哪个文件的拷贝”。
2009-9-18 17:57
0
雪    币: 635
活跃值: (101)
能力值: ( LV12,RANK:420 )
在线值:
发帖
回帖
粉丝
9
1.PsLoadedModuleList或ZwQuerySystemInformation获取的exe full path,确实就是那个exe当时在硬盘中的路径,这点是没有异议的
你之所以认为这两个是不同的,原因是你看的是base name,而不是full name,观察一下psloadedmodulelist的结构就可知道

2.从fileversion 中获取的source file name,是EXE“真正的”名称,这个通常在获取symbol时使用,因为symbol sever是要区分realname的,这个信息存储在DEBUG节中
2009-9-18 18:01
0
雪    币: 635
活跃值: (101)
能力值: ( LV12,RANK:420 )
在线值:
发帖
回帖
粉丝
10
楼主说的是没有问题的,可能表述上有问题,注意看楼主的方式2,他是需要从PDB获取这个信息,那么自然就需要真实的exe名,只是,这个真实的EXE,并不是此时在硬盘时的路径,而是安装时的路径,用这个路径在目标系统的磁盘上是找不到这个文件的,但微软的symbol server识别的是这个文件名
2009-9-18 18:04
0
雪    币: 722
活跃值: (123)
能力值: ( LV12,RANK:300 )
在线值:
发帖
回帖
粉丝
11
这样理解才是对的,这我在3楼已经说出来了,“原始的exe名”(指的是源文件名),和“硬盘上的PE文件名”本来就不是一个概念,不应该混在一起,这样很容易误导别人。
我本来的确认为楼主是表述不当,但是在我3楼已经那样说的情况下,楼主仍然坚持PsLoadedModuleList和NtQuerySystemInformation是错的,没有意识到这个表述的问题。

另外BaseDllName和FullDllName的区别我也是在前几天的一帖(http://bbs.pediy.com/showthread.php?t=97717)中发现了,我也在想楼主是不是因为这个而误解了。
所以我说PsLoadedModuleList和NtQuerySystemInformation并没有错(NtQuerySystemInformation用的是FullDllName),因为它们只是告诉我们“现在加载的文件在硬盘中是哪一个文件”,而不是告诉我们“这个文件原来是哪个文件的拷贝”

所以我在8楼也举了1.exe和2.exe的例子,以下对加载方式的两种说法是不一样的
1. 程序把1.exe拷贝成2.exe,然后加载2.exe
2. 程序在某些环境下不加载2.exe而直接加载1.exe

对于系统内核,应该是第一种情况,而楼主的帖子中
当系统为PAE模式时,系统会自动将ntkrnlpa.exe解析成ntkrnlpa.exe(单CPU内核)或ntkrpamp.exe(多CPU内核)
当系统为非PAE模式时,系统会自动将ntoskrnl.exe解析成ntoskrnl.exe (单CPU内核)或ntkrnlmp.exe (多CPU内核)

这样的表述极容易让人误解为系统内核加载的方式是第二种。
2009-9-18 18:08
0
雪    币: 722
活跃值: (123)
能力值: ( LV12,RANK:300 )
在线值:
发帖
回帖
粉丝
12
关于这一点,《软件调试》8.4.3节的表述,就比楼主这样的表述更清晰,更不会引起误解:
……首先安装程序在安装时会根据系统中的处理器个数,选择单处理器版本或多处理器版本中的一个复制到用户系统中(system32目录)。如果复制的是多处理器版本,那么会将其改为与单处理器版本相同的名字。这也是我们上面强调“原始文件名”的原因,它是相对安装在用户系统后的文件名而言的。

这不就既明明白白地指出了这个过程,也指出了两个文件名概念上的区别。
2009-9-18 18:32
0
雪    币: 258
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
13
以我的机器为例,XP SP3 gdr PAE 多核
PsLoadedModuleList获取短名字是:ntoskrnl.exe  长名字是:sys32\ntkrnlpa.exe
而实际上,真正的内核Image是ntkrpamp.exe
这点区别,在Load Image做PE解析的时候,并不重要,但是在涉及.pdb的时候,却很重要
调用.pdb来解析的时候,也应该是根据ntkrpamp.exe来Load .pdb解析
在多核(或从多核降低到单核)环境下,C:\WINDOWS\system32\ntkrnlpa.exe的确是C:\WINDOWS\system32\ntkrpamp.exe,但其.pdb却与ntkrnlpa.exe无关,应该根据ntkrpamp.exe和其版本号来确定,即ntkrpamp.exe+5.1.2600.5755 (xpsp_sp3_gdr.090206-1234)唯一确定真实Image
截个图给大家看一下,windbg的symbol缓存,也是分得如此清楚的,乱了就载入错误的pdb了,所以我们才要精确区分内核文件。
上传的附件:
  • 1.JPG (46.65kb,861次下载)
2009-9-18 18:34
0
雪    币: 722
活跃值: (123)
能力值: ( LV12,RANK:300 )
在线值:
发帖
回帖
粉丝
14
楼主还没明白我们的意思。
我们的意思是pdb的确是ntkrpamp.pdb,那是因为pdb是根据原始文件名来的。
但是磁盘上的文件确确实实是ntkrnlpa.exe,你不能说磁盘上的文件不是ntkrnlpa.exe而是ntkrpamp.exe,你的表述很容易给人以这样的误解。
2009-9-18 18:39
0
雪    币: 722
活跃值: (123)
能力值: ( LV12,RANK:300 )
在线值:
发帖
回帖
粉丝
15
C:\WINDOWS\system32\ntkrnlpa.exe的确是C:\WINDOWS\system32\ntkrpamp.exe,但其.pdb却与ntkrnlpa.exe无关,应该根据ntkrpamp.exe和其版本号来确定,即ntkrpamp.exe+5.1.2600.5755 (xpsp_sp3_gdr.090206-1234)唯一确定真实Image

请问这里的“应该根据ntkrpamp.exe和其版本号来确定”中的“ntkrpamp.exe”,到底是指的“磁盘上的C:\WINDOWS\system32\ntkrpamp.exe文件”(继承前面的语境),还是指“ntkrpamp.exe这个文件名字符串”?
2009-9-18 18:47
0
雪    币: 258
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
16
字符串

因为我需要的是根据文件名字(字符串)及具体版本(X.X.XXX)gdr还是qfe找到pdb
2009-9-18 20:42
0
雪    币: 258
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
17
在你的语境中所假设的那种情况下,磁盘上的内核文件,其文件名是ntkrnlpa.exe,但其实质(包括里面的函数,及我们需要使用的pdb)是ntkrpamp.exe(ntkrpamp.pdb)
我的意思是说,PsLoadedModuleList获得的内核名字,不是我们实际要Load的pdb名字,所以,我们应该去解析PE结构,得到InternalName和FileVersion,由这2个东西来确定具体的pdb。
当然,用API也可以获得InternalName和FileVersion,但是做ARK的时候,能自己实现的函数,如果并不复杂的话,当然要自己实现。
如果有误解,偶在这里道个歉,呵呵。
2009-9-18 21:02
0
雪    币: 258
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
18
从其关联的pdb角度来说,我们可以说“磁盘上的文件不是ntkrnlpa.exe而是ntkrpamp.exe”
2009-9-18 21:04
0
雪    币: 258
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
19
谢谢,已经修正文章了。
2009-9-18 21:09
0
雪    币: 722
活跃值: (123)
能力值: ( LV12,RANK:300 )
在线值:
发帖
回帖
粉丝
20
是字符串就对了,最好加上引号表示。
所以你要看到,我们的确理解你在说什么(因为这个问题我们自己也知道),但是如果假设是一个不了解这个问题的读者,那么他从你的表述中就很容易产生一些误会(哪里是指磁盘文件名,哪里是指源文件,哪里是指文件名字符串……)。如果这些内容要写成书,那就更加容易让读者误解了。因此我引述《软件调试》中的那句话,就是说明在表达方式上如何做得更好,因为我们写帖子跟大家分享,也是希望大家能够读懂我们的意思。

同样在《软件调试》的那一节,在我之前引述的文字后面有一段写道:
通过文件属性对话框中版本页(Version Tab)的Original File Name,可以观察到内核文件的原始名称。

显然,楼主这篇帖子要指出的原理(楼主给出的代码也就是这个原理的代码实现),就在这里了,而且表述得干净利落没有歧义。
2009-9-18 21:13
0
雪    币: 258
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
21
恩,谢谢大家的关心和指正。
2009-9-18 21:19
0
雪    币: 722
活跃值: (123)
能力值: ( LV12,RANK:300 )
在线值:
发帖
回帖
粉丝
22
可你原来的表述没有加上“从其关联的pdb角度”啊。
因此正确的表述是“我所要的pdb不是ntkrnlpa.pdb而是ntkrpamp.pdb”,毕竟“磁盘上的文件”和“所需要的pdb文件”这两个概念不是一回事

而且如果没有把“磁盘文件”和“源文件名”以及“pdb文件名”分开来,会引起误解,误解在哪呢?
实际上系统安装过程中把ntkrpamp.exe改名为ntkrnlpa.exe后,system32文件夹不一定会存在ntkrpamp.exe文件。即使存在,它甚至也不一定与ntkrnlpa.exe是相同的。
C:\WINDOWS\system32\ntkrnlpa.exe的确是C:\WINDOWS\system32\ntkrpamp.exe

你这句话如果指的是磁盘文件,就又不对了。
我的系统环境跟你基本一致,我是从SP2升到SP3的,我发现我的C:\WINDOWS\system32\ntkrpamp.exe居然还是SP2的版本,而C:\WINDOWS\system32\ntkrnlpa.exe则是SP3的版本。
这说明SP2升到SP3时,安装程序把安装包里的ntkrpamp.exe释放覆盖了C:\WINDOWS\system32\ntkrnlpa.exe,但是没有覆盖C:\WINDOWS\system32\ntkrpamp.exe

因此,如果不把“磁盘文件”和“源文件名字符串”这两个概念分开的话,读者很容易认为是要从磁盘文件C:\WINDOWS\system32\ntkrpamp.exe中找寻版本信息并加载pdb,这就错了。
2009-9-18 21:24
0
雪    币: 258
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
23
又更新了,请大家看看。
2009-9-18 22:57
0
雪    币: 235
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
24
小聪大侠,真是个认真,热心的人。佩服!
2010-2-7 13:01
0
雪    币: 49
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
debug节中不是已经存储了pdb所有信息了吗,用不着自己分析文件名啊。
2010-12-3 13:52
0
游客
登录 | 注册 方可回帖
返回
//