freesoft00
当前离线
UID
-
129513
在线时间
-
490 小时
帖子
-
1290
积分
-
2589
技术
-
0
魅力
-
0
人气
-
21
活力
-
1202
性别
-
保密
经验
-
2589
阅读权限
-
30
最后登录
-
2012-6-29
主题
-
57
精华
-
0
![Rank: 3](http://a.ikafan.com/image/common/star_level2.gif) ![Rank: 3](http://a.ikafan.com/image/common/star_level1.gif)
积分
-
2589
技术
-
0
魅力
-
0
人气
-
21
注册时间
-
2006-12-7
|
调用ZwQueryVirtualMemory遍历进程模块示例(更新)昨天发了个改自身PEB欺骗SREng的示例,本来只是纯属自娱自乐,结果大家的反馈让我大吃一惊,原来有这么多Ring3甚至Ring0的程序,在获取进程模块全文件名时都偷懒了,这实在令人大感意外。 也许大家都觉得,隐藏进程和检测隐藏进程才是王道,却没有注意到,恶意软件进程有时根本不需要隐藏,只要能给自己披上一个伪装,让安全软件把它的进程当成是正常的,就足够了。在现今杀毒软件普遍搞主动防御的情况下,这种情况更为明显。HIPS对恶意软件的行为,不是拦截不住,而是因为它“看起来正常”,而被放过了。我昨天发那个示例,其实本来就是想要让大家意识到这一点。 不过无论如何,还得自己收尾,Ring3情况下怎么对付这种伎俩?学IceSword,ZwQueryVirtualMemory。只不过IceSword是在驱动层调用(ntoskrnl.exe没有导出,要自己从SSDT中找地址),其实Ring3情况下也可以调用的。 因此我今天再给一个示例,可以把之前发的PEB.exe的真身给揭发出来。 一直只知道IceSword(以及看雪论坛上开源的山寨版IceSword)是用ZwQueryVirtualMemory来遍历进程的虚拟内存,从而得到加载的模块的,不过从来没有自己试过。 看了山寨版IceSword相关代码,原来是在Ring3中实现的,就想自己动手整一整,结果发现原来不是那么难。于是用汇编写了个Win32控制台程序来做演示。 基本原理不复杂,倒是细枝末节耗去很多时间。 首先我是以区段内存对齐的单位0x1000字节进行遍历,这样会出现多个区段属于同一个文件。这样我使用两次调用ZwQueryVirtualMemory: 第一次使用参数MemoryBasicInformation,判断传回的MEMORY_BASIC_INFORMATION结构中的AllocationBase字段值与传入的Base是否相等,相等则说明这是文件的第一个区段,这时才进行第二次调用; 第二次使用参数MemorySectionName,获得区段文件名。 山寨版IceSword使用的是0x10000字节为间隔,似乎就避免了这个麻烦,而且也不会漏掉模块。 第一次写Win32控制台程序,也是第一次用msvcrt.dll中的函数(由masm32rt.inc中封装)做字符串处理,发现自己这一块居然超不熟,耗费了不少时间。 其实说原创也不是太合适了,因为参考了一些网上的代码,比如山寨版IceSword,还有提SeDebugPrivilege的那部分也是直接拿的网上的代码(其实只要不是访问系统进程,提权不是必须的)。 代码写得比较挫,又一次证实了自己在Win32汇编这块还很菜…… 调用方法:把进程ID作为命令行参数传入,如Query.exe 1000 update:这帖发看雪之后,引来几位牛人回帖,也算值了 如海风月影回帖指出不支持非本地磁盘盘符解析的毛病,这个是我之前偷懒了。 看了看雪上的回帖之后又修改了一下,主要是在盘符转换方面不再偷懒,现在应该可以支持非本地磁盘了。 对这个功能,发现KsBinSword好像没有做? 网上搜索发现QueryDosDevice,但据说没有功能反过来的,这样只能先对A-Z的盘符调用QueryDosDevice,把结果保存起来,然后需要转换的时候再跟全文件名比对。 另外调试了一下ZwQueryVirtualMemory的调用,因为我很在意那几个nls文件,记得好像说是内核创建进程过程中将其映射的。想看看怎么把它们跟后面的Module区分开来。 发现这几个nls映像头部的AllocationProtect都有PAGE_EXECUTE属性,而其他各个exe和dll的映像头部的AllocationProtect属性没有PAGE_EXECUTE,但两者当前的Protect属性都有PAGE_EXECUTE。 因此加了几行代码来做检测,在原文中被注释掉了,如果把注释掉的还原,则显示出来的模块中不包括nls文件。 复制内容到剪贴板 代码:;=============================================================================== ;use ZwQueryVirtualMemory to enum Modules of a process ;subsystem:console ;OS Platform:tested on Windows XP Professional simplified with Service Pack 3 ;轩辕小聪 http://hi.baidu.com/yicong2007 ; ;release notes: ; ;2009.04.06 ;改写ConvertDeviceStr函数,实现DosDevice与盘符的转换,以支持非本地磁盘盘符的获取。 ;遍历时只对具有PAGE_EXECUTE属性的内存查找MemorySectionName。 ;如将被注释掉的三处代码还原,可过滤掉nls文件的显示,但不知这样是否会漏掉模块。 ; ;2009.04.05 ;第一个版本 ; ;=============================================================================== include \masm32\include\masm32rt.inc include \masm32\include\w2k\native.inc includelib \masm32\lib\masm32.lib include \masm32\macros\macros.asm include \masm32\include\advapi32.inc includelib \masm32\lib\advapi32.lib .data PID dd 0 Base dd 0 hProcess dd 0 DataLength dd 0 lpszPID db 120 dup(0) lpoutdata db 300h dup(0) lpFileName db 120h dup(0) lpDeviceList dd 26d dup(0) lpListBuffer db MAX_PATH*26d dup(0) .code CreateDeviceList proto CreateDeviceList proc uses esi edi ebx local VolNum local i mov VolNum, 3A41h mov i, 0 mov esi, offset lpListBuffer mov edi, offset lpDeviceList .repeat invoke QueryDosDevice, addr VolNum, esi, MAX_PATH .if eax!=0 mov [edi], esi xor eax, eax mov byte ptr [esi+MAX_PATH-1], al .endif lea esi, [esi+MAX_PATH] lea edi, [edi+4] add VolNum, 1 add i, 1 .until i == 26d mov eax, 1 ret CreateDeviceList endp ConvertDeviceStr proto :DWORD, :DWORD ;===================================================== ;把得到的文件名中的盘符翻成平常的C、D……并将字符串改 ;成输出所需的格式 ;===================================================== ConvertDeviceStr proc uses esi edi ebx lpSource:DWORD, ImageBase:DWORD local VolNum local lptmp[MAX_PATH]:byte local i local match lea edi, lptmp xor eax, eax mov i, eax mov match, eax mov ecx, MAX_PATH rep stosb mov VolNum, 41h mov esi, offset lpDeviceList .repeat mov edi, [esi] .if edi!=0 invoke crt_strstr, lpSource, edi .if eax==lpSource mov match, 1 .break .endif .endif add esi, 4 add VolNum, 1 add i, 1 .until i==26d .if match==0 invoke crt_strncpy, addr lptmp, lpSource, MAX_PATH .else invoke crt_strlen, edi mov esi, lpSource lea esi, [esi+eax] invoke crt_sprintf, addr lptmp, CTXT('%s:%s'), addr VolNum, esi .endif invoke crt_sprintf, lpSource, CTXT('%08X %s',0dh,0ah), ImageBase, addr lptmp ret ConvertDeviceStr endp DebugPrivilege PROTO :DWORD ;===================================================== ;提SeDebugPrivilege,copy from network ;===================================================== DebugPrivilege proc uses esi edi ebx dwEnbled local hToken local tmpLuid:LUID,tkp:TOKEN_PRIVILEGES invoke GetCurrentProcess lea ebx,hToken invoke OpenProcessToken, eax, TOKEN_ADJUST_PRIVILEGES Or TOKEN_QUERY, ebx invoke LookupPrivilegeValue, NULL, CTXT('SeDebugPrivilege'), addr tmpLuid mov tkp.PrivilegeCount,1 push tmpLuid.LowPart pop tkp.Privileges[0].Luid.LowPart push tmpLuid.HighPart pop tkp.Privileges[0].Luid.HighPart .if dwEnbled mov tkp.Privileges[0].Attributes, SE_PRIVILEGE_ENABLED .else mov tkp.Privileges[0].Attributes,NULL .endif invoke AdjustTokenPrivileges, hToken, FALSE, addr tkp, sizeof TOKEN_PRIVILEGES, NULL, NULL invoke GetLastError .if eax == ERROR_SUCCESS push TRUE .else push FALSE .endif invoke CloseHandle,hToken pop eax ret DebugPrivilege endp Start proc uses esi edi ebx invoke GetCL, 1, offset lpszPID invoke crt_sscanf, offset lpszPID, CTXT('%d'), offset PID .if PID==0 invoke StdOut, CTXT('Invaild PID.') ret .endif invoke DebugPrivilege, 1 .if eax==FALSE invoke StdOut, CTXT('Enable SeDebugPrivilege failed.') ret .endif invoke OpenProcess, PROCESS_QUERY_INFORMATION, 0, PID .if eax==0 invoke StdOut, CTXT('OpenProcess failed.') ret .endif mov hProcess, eax invoke GetModuleHandle, CTXT('ntdll.dll') mov esi, eax invoke GetProcAddress, esi, CTXT('ZwQueryVirtualMemory') mov edi, eax invoke StdOut, CTXT('ModuleBase ImageFileName',0dh,0ah) invoke CreateDeviceList .repeat push offset DataLength push 300h push offset lpoutdata push MemoryBasicInformation push Base push hProcess call edi mov esi, offset lpoutdata assume esi:ptr MEMORY_BASIC_INFORMATION ;mov ebx, [esi].AllocationProtect ;and ebx, WSLE_PAGE_EXECUTE mov ecx, [esi].Protect and ecx, WSLE_PAGE_EXECUTE mov esi, [esi].AllocationBase assume esi:nothing .if eax==0 && esi==Base && ecx!=0 ;&& ebx==0 push offset DataLength push 300h push offset lpoutdata push MemorySectionName push Base push hProcess call edi .if eax==0 mov esi, offset lpoutdata assume esi:ptr MEMORY_SECTION_NAME movzx eax, [esi].SectionFileName._Length .if eax!=0 mov esi, [esi].SectionFileName.Buffer assume esi:nothing invoke crt_sprintf, offset lpFileName, CTXT('%ws'),esi invoke ConvertDeviceStr, offset lpFileName, Base invoke StdOut, offset lpFileName .endif .endif .endif mov eax, Base add eax, 1000h mov Base, eax .until Base==80000000h ret Start endp end Start [ 本帖最后由 freesoft00 于 2009-4-11 16:26 编辑 ] |
|