jununfly
驱动牛犊
驱动牛犊
  • 注册日期2008-10-17
  • 最后登录2010-06-01
  • 粉丝0
  • 关注0
  • 积分86分
  • 威望560点
  • 贡献值2点
  • 好评度0点
  • 原创分0分
  • 专家分0分
阅读:3075回复:7

进程名的一些事儿

楼主#
更多 发布于:2009-02-03 11:04
昨日听了大喔喔和LookSail的讨论有些想法,看了看网页就把OSR的这篇翻译了下,希望对有些同道有所帮助
有些不通的地方无伤大雅,原文语言组织太散有些地方也没说清,偶水平太低收不住。。。
What's in a (Process) Name? Obtaining A Useful Name for the Executable Image in a Process The NT Insider, Vol 13, Issue 4, July - August 2006 | Published: 19-Sep-06| Modified: 19-Sep-06

声明:原文来自www.osr.com,所有权利归原作者所有,翻译并贴在这里的目的只为学习和交流,除了商用你可以随意地使用这篇译文。但请不要删除声明。
                                                     ——by jununfly
版本:20090203

有些开发者需要或想要知道给定进程中正在执行的镜像的名。传统上,通常都是用PsGetProcessImageFile Name来达到这个目的的,它返回Windows OS用于维护每进程状态信息的EPROCESS结构中的一个域的内容。
如我们从本地调试器信息中看到的(图1),进程的镜像文件比EPROCESS结构中的一个域稍大一些。注意EPROCESS地址在EBP+8中,先整它 – 它也是此函数唯一的参数.
Connected to Windows XP 2600 x86 compatible target, ptr64 FALSESymbol search path is: srv*c:\symbols\websymbols*http://msdl.microsoft.com/download/symbolsExecutable search path is:*******************************************************************************WARNING: Local kernel debugging requires booting with /debug to work optimally.*******************************************************************************Windows XP Kernel Version 2600 (Service Pack 2) MP (2 procs) Free x86 compatibleProduct: winNt, suite: Terminalserver SingleUserTSBuilt by: 2600.xpsp_sp2_gdr.050301-1519Kernel base = 0x804d7000 PsLoadedModuleList = 0x805624a0Debug session time: Mon Aug  7 13:29:53.486 2006 (GMT-4)System Uptime: 2 days 11:08:38.140lkd> .reloadConnected to Windows XP 2600 x86 compatible target, ptr64 FALSELoading Kernel Symbols..............................................................................Loading User Symbols..............................................................................Loading unloaded module list.......................*** ERROR: Symbol file could not be found.  Defaulted to export symbols for Clkd> u nt!PsGetProcessImageFileNameNt!PsGetProcessImageFileName:8050a14a  8bff mov edi,edi8050a14c  55 push ebp8050a14d  8bec mov ebp,esp8050a14f  8b4508 mov eax,dword ptr [ebp+8]8050a152  0574010000 add eax,174h8050a157  5d pop ebp8050a158  c20400 ret 48050a15b  8bce mov ecx,esi
图1 – PsGetProcessImageFileName的本地调试信息    
不幸的是这个方法有一些问题:
尽管已知,但此函数是未公开的
这个域中包含的信息极其有限。它仅包含镜像文件名的首16(ASCII)个字符.
实际上第二个问题带来的麻烦很多,因为镜像文件的名本质上意味着什么都没有。例如,以前的内核模式驱动都通过检查这个域来使它们的服务生效。最怪异的现象是服务名叫svchost.exe的,它是一个经常骗人的公有的名。
建议我们建议另一个获得此信息的模式如图2
typedef NTSTATUS (*QUERY_INFO_PROCESS) (    __in HANDLE ProcessHandle,    __in PROCESSINFOCLASS ProcessInformationClass,    __out_bcount(ProcessInformationLength) PVOID ProcessInformation,    __in ULONG ProcessInformationLength,    __out_opt PULONG ReturnLength    );
QUERY_INFO_PROCESS ZwQueryInformationProcess;
NTSTATUS GetProcessImageName(PUNICODE_STRING ProcessImageName){    NTSTATUS status;    ULONG returnedLength;    ULONG bufferLength;    PVOID buffer;    PUNICODE_STRING imageName;        PAGED_CODE(); // this eliminates the possibility of the IDLE Thread/Process
    if (NULL == ZwQueryInformationProcess) {
        UNICODE_STRING routineName;
        RtlInitUnicodeString(&routineName, L"ZwQueryInformationProcess");
        ZwQueryInformationProcess =             (QUERY_INFO_PROCESS)MmGetSystemRoutineAddress(&routineName);
        if (NULL == ZwQueryInformationProcess) {            DbgPrint("Cannot resolve ZwQueryInformationProcess\n");        }    }    //    // Step one - get the size we need    //    status = ZwQueryInformationProcess( NtCurrentProcess(),                                         ProcessImageFileName,                                        NULL, // buffer                                        0, // buffer size                                        &returnedLength);
    if (STATUS_INFO_LENGTH_MISMATCH != status) {
        return status;
    }
    //    // Is the passed-in buffer going to be big enough for us?      // This function returns a single contguous buffer model...    //    bufferLength = returnedLength - sizeof(UNICODE_STRING);        if (ProcessImageName->MaximumLength < bufferLength) {
        ProcessImageName->Length = (USHORT) bufferLength;
        return STATUS_BUFFER_OVERFLOW;            }
    //    // If we get here, the buffer IS going to be big enough for us, so     // let's allocate some storage.    //    buffer = ExAllocatePoolWithTag(PagedPool, returnedLength, 'ipgD');
    if (NULL == buffer) {
        return STATUS_INSUFFICIENT_RESOURCES;            }
    //    // Now lets go get the data    //    status = ZwQueryInformationProcess( NtCurrentProcess(),                                         ProcessImageFileName,                                        buffer,                                        returnedLength,                                        &returnedLength);
    if (NT_SUCCESS(status)) {        //        // Ah, we got what we needed        //        imageName = (PUNICODE_STRING) buffer;
        RtlCopyUnicodeString(ProcessImageName, imageName);            }
    //    // free our buffer    //    ExFreePool(buffer);
    //    // And tell the caller what happened.    //        return status;    }
图2 – 一个新建议    
这个函数自身简单易懂.它依赖于一个未公开的函数(ZwQueryInformationProcess),但注意它的副本(NtQueryInformationProcess)是公开的。为了使用一个内核内存缓冲我们需要用Zw的那个。
关键点是为了在auditing 子系统中提供执行镜像的全路径名Windows总是存储这个信息。这个API就使用了现有的被存储的路径名。
其他进程的名这个函数被实现为提取当前进程的进程名。不过,你能用以下两种方法中的一种来获得一个不同进程的进程名:
1.  如果你有这个进程的一个句柄,你可以用那个值代替NtCurrentProcess()宏。(注意以我们的经验,我们通常有一个进程对象而非一个进程句柄 – 这俩不可互换).
2.  如果有一个EPROCESS地址,就能用KeStackAttachProcess或KeUnstackDetachProcess来绑定到进程上。此技术代价沉重,如果你需要正常地执行这个操作最好时缓存这些信息。
使用第二个技术时,一定要注意返回的名是一个被缓存的名。如果原始文件的名在其被缓存之后改变了,缓存的名保持不变。换句话说,如果执行镜像被重命名了,这通常是对一个正在运行的执行来说,返回名仍是原始文件的名。
这个问题并不唯一.我们已经在一些文件上发现了同类现象(例如,XP上的CIFS 客户端实现就这样做了).因此,针对类似事件,可能需要额外的处理比如通过一个FSFD来加以保护。例如,你可能遇到一个依赖于知道镜像的指定名的安全产品。
二中选一?还有其他选项,驱动也能跟踪进程创建或拆卸事件的注册(PsSetCreateProcessNotifyRoutine) 或镜像加载(PsSetLoadImageNotifyRoutine).
PsSetCreateProcessNotifyRoutine限制能注册使用这个API的驱动的数量。因为Windows OS中有一个固定大小的表,它可能会使此调用失败。如果失败了,驱动需要确保它能处理这样的情况。PsSetLoadImageNotifyRoutine也有同样的限制,但是是对所有的镜像加载来说,而不仅仅是原始进程镜像。因此,它包括了驱动,DLLs,执行等等。
综述综上这些方法都会提供一个有用的名因为它们包括全路径名。这比用EPROCESS结构中短的ASCII名要好很多。一句警告 – 如果你决定要用调试级的名,那就仅仅用于调试吧,因为它不可靠且不能被安全性检查所依赖。
我们挑选这个方法是因为它能工作于所有的情形,且不依赖于可能会失败的任何东西。在你的驱动中你可能会同时实现这个机制和一个基于缓存的依赖于进程创建逻辑的机制。

 进程名的一些事.rar
jununfly
驱动牛犊
驱动牛犊
  • 注册日期2008-10-17
  • 最后登录2010-06-01
  • 粉丝0
  • 关注0
  • 积分86分
  • 威望560点
  • 贡献值2点
  • 好评度0点
  • 原创分0分
  • 专家分0分
沙发#
发布于:2009-02-03 11:06
不贴不知道,一贴吓一跳,认错先,网页上的效果太乱了。。。
wshchb
驱动小牛
驱动小牛
  • 注册日期2006-05-14
  • 最后登录2015-03-05
  • 粉丝1
  • 关注0
  • 积分1005分
  • 威望385点
  • 贡献值1点
  • 好评度97点
  • 原创分0分
  • 专家分0分
板凳#
发布于:2009-02-04 15:39
学习学习
alwaysrun
驱动小牛
驱动小牛
  • 注册日期2006-06-01
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分1059分
  • 威望752点
  • 贡献值1点
  • 好评度98点
  • 原创分0分
  • 专家分0分
地板#
发布于:2009-02-12 11:23
楼主真勤快,谢谢,学习
一颗平常的心!
liuwuyang635
驱动牛犊
驱动牛犊
  • 注册日期2009-01-13
  • 最后登录2011-02-28
  • 粉丝0
  • 关注0
  • 积分5分
  • 威望51点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
地下室#
发布于:2009-02-18 11:03
蛮好的,比没有的强
谢谢LZ
darkseer
驱动牛犊
驱动牛犊
  • 注册日期2008-09-23
  • 最后登录2009-09-07
  • 粉丝0
  • 关注0
  • 积分2分
  • 威望21点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
5楼#
发布于:2009-08-27 17:21
学习,多谢分享!
neak47
驱动小牛
驱动小牛
  • 注册日期2009-05-25
  • 最后登录2016-01-09
  • 粉丝4
  • 关注0
  • 积分140分
  • 威望1221点
  • 贡献值1点
  • 好评度0点
  • 原创分0分
  • 专家分1分
6楼#
发布于:2009-08-27 17:23
好帖
alwaysrun
驱动小牛
驱动小牛
  • 注册日期2006-06-01
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分1059分
  • 威望752点
  • 贡献值1点
  • 好评度98点
  • 原创分0分
  • 专家分0分
7楼#
发布于:2011-03-03 09:56
好东西,学习了
一颗平常的心!
游客

返回顶部