DLL文件整个内存里只有一份??

MagicMoon 2007-08-01 10:35:43
对于DLL,书上是这么说的:

  “它们有助于节省内存。如果两个或多个应用程序使用同一个D L L,那么该D L L的页面只要放入R A M一次,所有的应用程序都可以共享它的各个页面。”
                         -摘自《windows核心编程》

  但是实际操作中,却有些疑问:
  我用APIHook时,必须针对每个我感兴趣的进程、对我感兴趣的API进行hook,而若果按照书中的说法,实际上我只需要对我感兴趣的API进行一次Hook就好了,跟进程无关。

  这么看起来,实际与书中的说法似乎有些矛盾。琢磨了半天,只想出一个可能性:copy-on-write机制(具体的机制《windows核心编程》里也有详细的说明)。

  So,自己动手写了个修改Ntdll.dll代码段的DLL,代码如下:
/*
  WriteCopy_Test.cpp
*/
#include <windows.h>

BOOL WINAPI DllMain(
HINSTANCE hinstDLL, // handle to the DLL module
DWORD fdwReason, // reason for calling function
LPVOID lpvReserved // reserved
)
{
if (DLL_PROCESS_ATTACH == fdwReason)
{
unsigned char ucBuf[1] = {0};
HMODULE hModule = GetModuleHandle("ntdll.dll");

typedef VOID (NTAPI *_DbgBreakPoint)(VOID);
_DbgBreakPoint DbgBreakPoint = (_DbgBreakPoint)GetProcAddress(hModule, "DbgBreakPoint");
memcpy(&ucBuf, DbgBreakPoint, 1);
DWORD dwOldProtect = 0;
VirtualProtect(DbgBreakPoint, 1, PAGE_EXECUTE_READWRITE, &dwOldProtect);
memcpy(DbgBreakPoint, &ucBuf, 1);
}
  //为了省内存,避免干扰,干脆连DLL_PROCESS_DETACH部分的代码也省略了
}

  我机子上的NtDll.dll文件大小为578KB,启动了一个基本上什么事都不干的小程序,该程序初始占用内存442KB。
  将上面的WriteCopy_Test.dll注入该程序,发现内存占用达到1524KB。

  测试的结果让我很迷惑,测试前后内存占用相差约1MB,除去NtDLL.dll的代码体积和一些WriteCopy.dll、注入程序申请的少许内存(不到1KB),还有500K去哪了?
是不是我分析的有问题?疑惑中。
...全文
725 26 打赏 收藏 转发到动态 举报
写回复
用AI写文章
26 条回复
切换为时间正序
请发表友善的回复…
发表回复
zenghao507 2011-08-30
  • 打赏
  • 举报
回复
mark 学习
MagicMoon 2007-08-13
  • 打赏
  • 举报
回复
:)
看了你的分析,很好,很强大~
我最近也会做些分析,可能会碰到些问题,到时候找你讨论吧(在你BLOG上留言,然后给你发CSDN消息)
julong88 2007-08-10
  • 打赏
  • 举报
回复
MARK
feimingbiao 2007-08-10
  • 打赏
  • 举报
回复
MagicMoon,向你学习,我也埋头真正钻研一下,赶下时髦,平生头一次写Blog,我的实验报告:

http://blog.csdn.net/feimingbiao/archive/2007/08/10/1736371.aspx

我觉得可以定论了,你不满意我们再研究。
MagicMoon 2007-08-09
  • 打赏
  • 举报
回复
感谢feimingbiao()
我现在写的测试程序就是用GetProcessMemoryInfo
等我得出了测试结果,立刻就发上来,并且通知您,希望您能继续指导我 :)
feimingbiao 2007-08-09
  • 打赏
  • 举报
回复
MagicMoon, 任务管理器可能会有噪音,你用GetProcessMemoryInfo 看看。

其实不用写加入修改共享代码段的代码,最简单是Debugger里面在Kernel 函数上设个断点就可以了,因为Debugger需要写

int 3 (cc)

所以一样会造成copy on write. 对了,这个也证实了你最早问题的答案:
"我用APIHook时,必须针对每个我感兴趣的进程、对我感兴趣的API进行hook,而若果按照书中的说法,实际上我只需要对我感兴趣的API进行一次Hook就好了,跟进程无关。"

如果是这样的话,你在一个程序里面设个CreateFile的断点,所有的程序就都停下来了。

MagicMoon 2007-08-09
  • 打赏
  • 举报
回复
feimingbiao,我用了你的方法,果然只有4KB的内存增量。多谢 :)
到目前为止,我只是证明了这样改会有一个内存页的增量,看上去似乎是copy-on-write
可是怎么拿出强有力的数据证据来证明我的问题的源头就是copy-on-write机制呢?
我查了下资料,copy-on-write机制是操作系统和CPU合作完成的,所以我很郁闷。
暂时还没想好怎么继续分析~
如果你有好点子,分享(期待你的跟帖,所以就不用“指导”一词了 :) )下
dx001 2007-08-09
  • 打赏
  • 举报
回复
学习了
feimingbiao 2007-08-09
  • 打赏
  • 举报
回复
MagicMoon, 千万别说指导,否则我不敢跟贴了。:) 我也是偷懒在这儿学习呢。挺佩服你的钻研精神的,不像我总不求甚解。

下班路上想了一下8K的问题,觉得我说的TaskManager噪音不是特别靠谱。又想了想,可能是你在启动程序的时候,因为系统状况不同,MFC的Share Library(MFCxx.dll)没有完全被Page In(因为那个DLL比较大,没有执行的代码可能没有完全载入或者被Page Out了)。你点Button的时候处理WM_COMMAND的那段儿CDialog的程序可能加载,用了一页。以下简单的程序不知道会不会再有8K现象:

main()
{
MessageBoxA(0,"start",0,0);
//看TaskManager
改DLL
MessageBoxA(0,"take another look", 0,0)
//看TaskManager
}
df8327 2007-08-08
  • 打赏
  • 举报
回复
关注
这个有意思:》
MagicMoon 2007-08-08
  • 打赏
  • 举报
回复
用VC6创建一个MFC APPWIZZARD工程,然后在某个按钮的OnOK函数里,加入修改共享代码段的代码。
等该程序正常启动,并内存占用稳定后(因为这个程序什么具体的事都不做,所以内存很快就会稳定下来。),点击OK按钮。在任务管理器里看到的就是4KB。
更细的测试环境我正在写,最近两天会再进行更细的测试。
feimingbiao 2007-08-08
  • 打赏
  • 举报
回复
MagicMoon,你是如何测试内存增长的?
MagicMoon 2007-08-07
  • 打赏
  • 举报
回复
感谢各位朋友的关注,特别是feimingbiao,
我十分认同您的说法。
这两天我也完善了我的测试环境,现在测试的结果多是4K,但是个别情况会出现8K的增长。
这个我就不明白了。

copy-on-write机制的单位是页,在我现在的机器上是4KB。所以我一旦修改了某个共享区某处,则copy-on-write机制就会把修改处所在的整个4KB都“搬”到进程私有空间里。可是为什么会出现8KB的情况?
十分期待您的解答!
wfenj 2007-08-05
  • 打赏
  • 举报
回复
我觉得DLL在内存里面只有一份,每个进程调用一次只是增加DLL的计数
feimingbiao 2007-08-05
  • 打赏
  • 举报
回复
cdeee(亦难),是你错了。 :) WinNT/2000/XP以后在物理内存里面也是一份儿。 和95/98的区别是当你改动DLL的时候,系统会另外做一个Copy。同样的代码一个进程里面一份儿拷贝没有任何意义。
cdeee 2007-08-04
  • 打赏
  • 举报
回复
关于内存相查 500kb 的问题,因该和你的驻入方式有关,可能是你的驻入程序在驻入 DLL 后没有处理好善后工作的原因。
此外,“它们有助于节省内存。如果两个或多个应用程序使用同一个D L L,那么该D L L的页面只要放入R A M一次,所有的应用程序都可以共享它的各个页面。”的说法是不正确的,估计你买的书说的是 Windows 9x 版本,而你使用的 却是 Windows NT/2000/XP/Vista 版本,在后者中, DLL 在内存中并不仅仅只有一份,实际上,在每个相关进程中都有一个该 DLL 的拷贝,这种拷贝不仅仅是数据段,还包括了代码段,当然,明确指定了的共享段是例外。
feimingbiao 2007-08-03
  • 打赏
  • 举报
回复
再说两句,这个Copy On Write 不只是进行Hook的时候才会发生,装载出现碰撞的时候也会发生,比如两个Dll都要装到一个位置,Loader只能搬走一个,被搬走的需要进行地址修复(全局变量,比如),这时候就会发生Copy On Write,这个DLL就自己占一份儿物理内存了。所以rebase好的话,可以减少物理内存使用。

对于系统的KnownDlls,都是特殊处理,每个Module有固定的装载位置(Vista为了防止病毒在固定地址乱下钩子随机了,但系统也是特殊处理还是共享物理内存)。通常的情况KnownDlls是不需要Copy on Write的,除了你这种Hook。这时候你修改的Page就会单独另外分配空间。

共享的通常只有代码段,数据段一写的瞬间就Copy on Write, fork出来另一个Page。
xdljf 2007-08-03
  • 打赏
  • 举报
回复
DLL共享的只是执行逻辑
feimingbiao 2007-08-03
  • 打赏
  • 举报
回复
"我用APIHook时,必须针对每个我感兴趣的进程、对我感兴趣的API进行hook,而若果按照书中的说法,实际上我只需要对我感兴趣的API进行一次Hook就好了,跟进程无关。"

不是这样的,你说的Copy On Write是对的。如果没有改动,Known DLLs是共享的,如果一个进程改动了,它就自己来一份儿了。
MagicMoon 2007-08-02
  • 打赏
  • 举报
回复
请问有朋友能帮我么?
加载更多回复(4)

15,467

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 进程/线程/DLL
社区管理员
  • 进程/线程/DLL社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧