Миша Басов дата публикации 07-04-2003 15:29 Несколько слов о загрузке DLL
Здравствуйте, коллеги! Поводом для написания этой статьи стало прочтение статьи Криса Касперски "Паковать или не паковать?".
Вкратце содержание статьи (дается в произвольном виде, со своими коментариями).
Все исполняемые модули (EXE и DLL) грузятся в память Windows(NT/2000/XP) следующим образом (я оставил только важные для нас пункты)
- Загрузка первой копии приложения:
-
- Прочитать служебную информацию из файла.
- Спроецировать в память все секции файла с защитой PAGE_EXECUTE_WRITECOPY(ну, кроме данных)
- Некоторые дополнительные приготовления (о них речь и пойдет в статье)
- Модуль готов.
- Загрузка всех последующих копий приложения:
-
- Прочитать служебную информацию из файла.
- Спроецировать в память все секции файла с защитой PAGE_EXECUTE_WRITECOPY(ну кроме данных...), здесь система ведет себя несколько по-другому, нежели при первой загрузке, поэтому я выделил ее в другой блок, но это тонкости.
- Некоторые дополнительные приготовления(о них речь и пойдет в статье)
- Модуль готов.
Отличий, вроде бы, никаких? Но (!!!) пункт 2 говорит, что память выделяется всем копиям одна и та же(!!!).
Таково свойство проецируемых файлов(см. Help. Topic: CreateFileMapping, OpenFileMapping, MapViewOfFile …).
"А как же данные каждого приложения, которые не зависимы от других приложений?"- спросите Вы. А для этого и стоит защита.
Как только программа пытается писать что-то в память, система делает копию этой страницы, ставит ей соответствующую защиту, и далее это приложение работает со своей (измененной) страницей, а все остальные с общей. Зачем так сложно? Из экономии памяти и увеличения быстродействия, ведь когда идет SWAP памяти, не измененные страницы система просто удаляет (ведь они остались в исполняемом файле), а измененные скачиваются в SWAP-файл. Когда данные опять понадобятся, они читаются из разных мест (из исполняемого файла или из SWAP-файла).
В первом случае мы имеем огромный плюс:
- Нет записи в SWAP-файл (а запись, между прочим, примерно в 3 раза медленнее, чем чтение),
- Не расходуется виртуальная память.
Теперь про упаковку файла. После проецирования, прежде чем модуль будет готов, он распаковывается специальной подпрограммой. Т.е. сразу при загрузке модуль переписывает всю (!!!) свою память, что заставляет систему выделить ее (память) в отдельный блок. Т.е. ни о какой экономии речь уже не идет. Ладно, если Вы запустили упакованный таким образом NotePad, а если Word? Да еще и 3 раза?
А теперь, непосредственно по теме данной статьи.
Хорошо. Мы вняли голосу умного человека и не стали паковать файл(ы). И казалось бы, все хорошо. НО Ваш проект устроен так, что он использует кучу DLL, которые Вы сами и написали. И у всех у них базовый адрес стоит $10000000(0х10000000-на CPP).
А теперь вернемся к загрузке (точнее к пункту 3), попробуем понять что такое базовый адрес и зачем он нужен.
В любой программе есть инструкции, которые привязаны к адресу.
- Например:
-
По адресу $1000000 у нас находится переменная "X";
Где-то мы к ней обращаемся.
...
org 1000000h
X dword ?
Y dword ?
...
mov eax,[1000000h]
inc eax
mov [1000000h],eax
...
| |
А теперь представим ситуацию, что загрузили модуль по другому адресу. Для примера, на 4 байта выше. Получим следующее представление:
...
org 0FFFFFCh
X dword ?
Y dword ?
...
mov eax,[1000000h]
inc eax
mov [1000000h],eax
...
| |
Смотрим и видим, что программа обращается уже не к переменной X, а к переменной Y. Что совершенно поломает всю логику работы программы. Что делать? Правильно. При загрузке по другому адресу надо аккуратно исправить все такие инструкции. Для этого в модулях есть все данные: Базовый адрес загрузки(Base Address), и таблица перемещений(Relocation Section). После проецирования (шаг 2) система исполняет шаг 3, т.е. если по базовому адресу модуль загрузить не удалось (система всегда сначала пытается загрузить модуль по базовому адресу), то она пытается загрузить его по другому адресу, используя данные о базовом адресе, о действительном адресе и данные из таблицы перемещений(пытается, потому что таблицы перемещений может не быть, тогда говорят, что модуль имеет фиксированный базовый адрес, и загрузить его по другому адресу не возможно). Процесс загрузки по другому адресу долгий. Система пробегает по всему коду, и исправляет адреса на правильные, а таких адресов может быть десятки и сотни тысяч(!!!).
Ну, Вы уже поняли "где собака порылась"? Подсказываю, исправляет код - значит записывает туда другое значение. А теперь понятно? Правильно. Опять вся память модуля летает в SWAP и назад. И системе совершенно все равно, по какой причине произошла запись в код: при распаковке или при исправлении кода. Все равно этот экземпляр уже лежит в памяти "тяжелым грузом".
Причем, как показывает практика, таких DLL(а это относится на 99.9% к ним, т.к. до загрузки EXE в памяти процесса вообще больше ничего нет, и его(EXE) можно грузить куда угодно и по любому адресу), в системе может набираться на мегабайты. У меня например таких DLL набралось на 23М :((((((. Т.е. почти 10% физической памяти(у меня стоит 256M :)))))). Но мне хорошо. Винт быстрый, и 10% это не смертельно. А каково тем, у кого 64М? В конце статьи пример распечатки загруженных DLL для Explorer(Проводника). Жирным выделены модули, загруженные не по базовым адресам. Общая длина модулей ~1.8 метра.:(((((((
Причем самое странное, что не только рядовые программеры не заботятся об этой проблеме (извините, народ, но мы почти все, и я в том числе, относимся к рядовым) но и "бренды" вроде Касперского, Intel, и др. делают то же самое.
Как исправить создавшееся положение? К сожалению, готового решения данной проблемы у меня нет. С Visual Studio идет программа ReBase.exe, которая изменяет базовый адрес указанного модуля. Но это надо сидеть и аккуратно все(!!!) DLL исправлять. А их у меня в системе, более 5 тысяч. Поэтому этот вопрос был, есть, и "будет есть". А эта статья призвана убедить Вас уменьшить обьем хаоса в этом мире. Конечно, разные разработчики вполне могут загнать свои DLL по одному и тому же адресу. Поэтому я, для себя, например, выбрал такую тактику, все свои DLL я гружу, начиная с адреса $20000000, причем ни одна DLL не пересекается с другой, даже из разных проектов. Для этого, правда, приходиться иметь базу данных уже использованных адресов. Как показывает практика и анализ процессов в системе, системные DLL Windows имеют разный базовый адрес. Более того, некоторые из них имеют фиксированный базовый адрес(см. Пример). А также, можно заметить, что с адреса $20000000 и до адреса $30000000 с копейками, пустое пространство. Вот это место я себе и облюбовал :)))).
Вывод. В системе всегда есть "плохие" модули. Но если Вы свои модули будете разносить по разным адресам, то и стандартные модули будут грузиться по базовым адресам, и в конечном итоге Ваша система будет работать быстрее и эфективнее.
Михаил Басов
Usage Image Addr Base Addr Size Module
1 00B10000 10000000 32768 F:\PROGRA~1\Adobe\ACROBA~1\Reader\ActiveX\AcroIEHelper.ocx
1 00F00000 10000000 24576 F:\PROGRA~1\Logitech\MOUSEW~1\SYSTEM\LGMOUSHK.dll
Fixed 01000000 1011712 F:\WINDOWS\Explorer.EXE
2 01750000 10000000 552960 F:\WINDOWS\System32\NOVNPNT.DLL
2 017E0000 10000000 221184 F:\WINDOWS\System32\MAPBASE.dll
1 018E0000 6A400000 65536 F:\WINDOWS\System32\NLS\ENGLISH\NWSHLXNR.DLL
2 01A20000 10000000 184320 F:\WINDOWS\System32\NWSHLXNT.dll
1 01D10000 6A400000 225280 F:\WINDOWS\System32\NLS\ENGLISH\NOVNPNTR.DLL
1 01ED0000 10000000 114688 F:\PROGRA~1\Common Files\Adobe\Shell\PSICON.DLL
1 02210000 10000000 53248 F:\PROGRA~1\KASPER~1\ANTIVI~1\avpshlex.dll
1 02230000 01500000 147456 F:\Program Files\WinRAR\rarext.dll
1 02A70000 10000000 217088 F:\Program Files\7-ZIP\7-zipn.dll
1 0FFD0000 139264 F:\WINDOWS\System32\rsaenh.dll
1 10000000 16384 F:\Program Files\Punto Switcher\correct.dll
4 1F7B0000 200704 F:\WINDOWS\System32\ODBC32.dll
1 1F850000 90112 F:\WINDOWS\System32\odbcint.dll
1 32270000 28672 F:\Program Files\Miranda\Plugins\BOSSKEY.DLL
1 32520000 73728 F:\Program Files\Microsoft Office\Office10\msohev.dll
8 50D00000 86016 F:\WINDOWS\System32\CLNWIN32.DLL
6 50D20000 163840 F:\WINDOWS\System32\CALWIN32.DLL
6 50D50000 282624 F:\WINDOWS\System32\NETWIN32.DLL
6 50DA0000 45056 F:\WINDOWS\System32\CLXWIN32.DLL
6 50DB0000 167936 F:\WINDOWS\System32\NCPWIN32.dll
16 50DF0000 131072 F:\WINDOWS\System32\LOCWIN32.DLL
Fixed 5AD70000 212992 F:\WINDOWS\System32\UxTheme.dll
1 5B630000 458752 F:\WINDOWS\System32\themeui.dll
1 68880000 258048 F:\WINDOWS\System32\hnetcfg.dll
1 6A400000 110592 F:\WINDOWS\System32\NLS\ENGLISH\MAPBASER.DLL
2 6C1B0000 274432 F:\WINDOWS\System32\DUSER.dll
16 71AA0000 32768 F:\WINDOWS\system32\WS2HELP.dll
26 71AB0000 86016 F:\WINDOWS\system32\WS2_32.dll
2 71AD0000 32768 F:\WINDOWS\System32\WSOCK32.dll
12 71B20000 69632 F:\WINDOWS\system32\MPR.dll
4 71BF0000 69632 F:\WINDOWS\System32\SAMLIB.dll
1 71C10000 53248 F:\WINDOWS\System32\ntlanman.dll
31 71C20000 323584 F:\WINDOWS\System32\NETAPI32.dll
2 71C80000 24576 F:\WINDOWS\System32\NETRAP.dll
1 71C90000 245760 F:\WINDOWS\System32\NETUI1.dll
2 71CD0000 90112 F:\WINDOWS\System32\NETUI0.dll
1 71D40000 110592 F:\WINDOWS\System32\ACTXPRXY.DLL
2 72410000 102400 F:\WINDOWS\System32\mydocs.dll
1 72430000 73728 F:\WINDOWS\System32\browselc.dll
2 72D10000 32768 F:\WINDOWS\System32\msacm32.drv
4 72D20000 36864 F:\WINDOWS\System32\wdmaud.drv
2 73000000 143360 F:\WINDOWS\System32\WINSPOOL.DRV
1 73380000 331776 F:\WINDOWS\System32\zipfldr.dll
1 74770000 585728 F:\WINDOWS\System32\MLANG.dll
2 74AD0000 28672 F:\WINDOWS\System32\POWRPROF.dll
1 74AE0000 28672 F:\WINDOWS\System32\CFGMGR32.dll
1 74AF0000 36864 F:\WINDOWS\System32\BatMeter.dll
1 74B00000 131072 F:\WINDOWS\System32\stobject.dll
1 74B30000 266240 F:\WINDOWS\System32\webcheck.dll
1 74B80000 532480 F:\WINDOWS\System32\printui.dll
1 74ED0000 61440 F:\WINDOWS\System32\wbem\wbemsvc.dll
1 74EF0000 40960 F:\WINDOWS\System32\wbem\wbemprox.dll
1 74FC0000 65536 F:\WINDOWS\System32\CLUSAPI.dll
2 75290000 229376 F:\WINDOWS\System32\wbem\wbemcomn.dll
1 755F0000 593920 F:\WINDOWS\System32\netcfgx.dll
1 75690000 598016 F:\WINDOWS\System32\wbem\fastprox.dll
4 75970000 987136 F:\WINDOWS\System32\MSGINA.dll
10 75A70000 667648 F:\WINDOWS\system32\USERENV.dll
2 75CF0000 1638400 F:\WINDOWS\system32\NETSHELL.dll
1 75E90000 659456 F:\WINDOWS\System32\SXS.DLL
2 75F40000 118784 F:\WINDOWS\system32\appHelp.dll
1 75F60000 24576 F:\WINDOWS\System32\drprov.dll
1 75F70000 36864 F:\WINDOWS\System32\davclnt.dll
Fixed 75F80000 1032192 F:\WINDOWS\System32\BROWSEUI.dll
1 760F0000 491520 F:\WINDOWS\System32\urlmon.dll
1 76170000 557056 F:\WINDOWS\System32\shdoclc.dll
2 76200000 618496 F:\WINDOWS\system32\WININET.dll
5 762A0000 61440 F:\WINDOWS\system32\MSASN1.dll
5 762C0000 565248 F:\WINDOWS\system32\CRYPT32.dll
11 76360000 61440 F:\WINDOWS\System32\WINSTA.dll
2 76380000 20480 F:\WINDOWS\System32\MSIMG32.dll
5 763B0000 282624 F:\WINDOWS\system32\comdlg32.dll
2 76400000 2076672 F:\WINDOWS\System32\msi.dll
5 76600000 110592 F:\WINDOWS\System32\CSCDLL.dll
3 76620000 319488 F:\WINDOWS\System32\cscui.dll
7 76670000 933888 F:\WINDOWS\System32\SETUPAPI.dll
1 76980000 28672 F:\WINDOWS\System32\LINKINFO.dll
5 76990000 147456 F:\WINDOWS\System32\ntshrui.dll
Fixed 769C0000 1347584 F:\WINDOWS\System32\SHDOCVW.dll
8 76B20000 86016 F:\WINDOWS\System32\ATL.DLL
17 76B40000 180224 F:\WINDOWS\System32\WINMM.dll
2 76C00000 184320 F:\WINDOWS\system32\credui.dll
1 76C30000 176128 F:\WINDOWS\System32\WINTRUST.dll
1 76C90000 139264 F:\WINDOWS\system32\IMAGEHLP.dll
2 76D30000 16384 F:\WINDOWS\system32\WMI.dll
2 76D40000 90112 F:\WINDOWS\system32\MPRAPI.dll
7 76D60000 86016 F:\WINDOWS\system32\iphlpapi.dll
3 76D80000 106496 F:\WINDOWS\system32\DHCPCSVC.DLL
2 76DA0000 196608 F:\WINDOWS\system32\WZCSvc.DLL
2 76DE0000 155648 F:\WINDOWS\system32\netman.dll
4 76E10000 147456 F:\WINDOWS\system32\adsldpc.dll
3 76E40000 192512 F:\WINDOWS\system32\ACTIVEDS.dll
9 76E80000 53248 F:\WINDOWS\system32\rtutils.dll
4 76E90000 69632 F:\WINDOWS\system32\rasman.dll
4 76EB0000 172032 F:\WINDOWS\system32\TAPI32.dll
5 76EE0000 225280 F:\WINDOWS\system32\RASAPI32.dll
3 76F20000 151552 F:\WINDOWS\system32\DNSAPI.dll
3 76F50000 32768 F:\WINDOWS\System32\WTSAPI32.dll
3 76F60000 180224 F:\WINDOWS\system32\WLDAP32.dll
9 76F90000 65536 F:\WINDOWS\System32\Secur32.dll
2 76FD0000 491520 F:\WINDOWS\System32\CLBCATQ.DLL
2 77050000 806912 F:\WINDOWS\System32\COMRes.dll
Fixed 77120000 569344 F:\WINDOWS\system32\OLEAUT32.dll
Fixed 771B0000 1155072 F:\WINDOWS\system32\ole32.dll
Fixed 772D0000 405504 F:\WINDOWS\system32\SHLWAPI.dll
14 77340000 569344 F:\WINDOWS\system32\comctl32.dll
Fixed 773D0000 8339456 F:\WINDOWS\system32\SHELL32.dll
1 77BD0000 28672 F:\WINDOWS\System32\midimap.dll
2 77BE0000 81920 F:\WINDOWS\System32\MSACM32.dll
10 77C00000 28672 F:\WINDOWS\system32\VERSION.dll
Fixed 77C10000 339968 F:\WINDOWS\system32\msvcrt.dll
Fixed 77C70000 262144 F:\WINDOWS\system32\GDI32.dll
Fixed 77CC0000 479232 F:\WINDOWS\system32\RPCRT4.dll
Fixed 77D40000 577536 F:\WINDOWS\system32\USER32.dll
Fixed 77DD0000 569344 F:\WINDOWS\system32\ADVAPI32.dll
Fixed 77E60000 937984 F:\WINDOWS\system32\kernel32.dll
Fixed 77F50000 692224 F:\WINDOWS\System32\ntdll.dll
| |
Примечание:
- 1-я колонка: Fixed-Нет таблицы перемещения, 1,2,3..-сколько раз модуль был загружен
- 2-я колонка: Адрес по которому загружен модуль
- 3-я колонка: Базовый адрес модуля(если пуст, то модуль загружен по базовому адресу)
- 4-я колонка: Размер модуля в байтах
- 5-я колонка: Полный путь к модулю
К материалу прилагаются файлы:
[Использование и создание DLL] [Параметры процесса/приложения]
Обсуждение материала [ 17-07-2008 11:56 ] 17 сообщений |