Всем привет. Нашёл способ тут http://www.slesh.name/?act=articles&subact=show&nid=12 Всё получилось и работает НО у меня возник вопрос, зачем автор грузит дллку по частям (хидеры, секции ...) ? Разве нельзя весь образ разом грузануть, а потом уже по нему пройтись и заполнить структуры ? вот кусок с вопросом Код (Text): ULONG retadr = 0; DWORD rb; HANDLE _hFile; IMAGE_DOS_HEADER DosHeader; IMAGE_NT_HEADERS PeHeader; IMAGE_SECTION_HEADER Section[10]; char tmp[1024]; _hFile = CreateFile(_filename, // открываемый файл GENERIC_READ|GENERIC_WRITE, // открываем для чтения/записи 0, // совместно не используется NULL, // защита по умолчанию OPEN_EXISTING, // только существующий файл FILE_ATTRIBUTE_NORMAL, // обычный файл NULL); // атрибутов шаблона нет if(_hFile==INVALID_HANDLE_VALUE)_hFile=0; if(_hFile) { // Читаем DOS заголовок ReadFile(_hFile, &DosHeader, sizeof(IMAGE_DOS_HEADER), &rb, NULL); if(DosHeader.e_magic == IMAGE_DOS_SIGNATURE) // проверим DOS сигнатуру { // если есть какимето данные между DOS заголовком и PE // то считаем их. В MS компиляторах это часто Rich данные if(sizeof(IMAGE_DOS_HEADER) < DosHeader.e_lfanew) { ReadFile(_hFile, &tmp[0], DosHeader.e_lfanew - sizeof(IMAGE_DOS_HEADER), &rb, 0); } // установим указатель в файле на PE заголовок SetFilePointer(_hFile, DosHeader.e_lfanew, NULL, FILE_BEGIN); // Читаем PE заголовок ReadFile(_hFile, &PeHeader, sizeof(IMAGE_NT_HEADERS), &rb, NULL); if(PeHeader.Signature == IMAGE_NT_SIGNATURE)// проверим PE сигнатуру { // считаем 10 секций ReadFile( _hFile, &Section[0], sizeof(IMAGE_SECTION_HEADER) * PeHeader.FileHeader.NumberOfSections, &rb, NULL); // выделим памяти столько, сколько указано в SIZE OF BASE retadr = (ULONG)VirtualAlloc( 0, PeHeader.OptionalHeader.SizeOfImage, MEM_COMMIT|MEM_RESERVE|MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE); if(retadr) // если память выделилась { // скопируем туда DOS заголовок memcpy((void *)retadr, &DosHeader, sizeof(IMAGE_DOS_HEADER)); // скопируем туда PE заголовок memcpy((void *)(retadr + DosHeader.e_lfanew), &PeHeader, sizeof(IMAGE_NT_HEADERS)); // скопируем туда таблицу секций memcpy((void *)(retadr + DosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS)), &Section[0], sizeof(IMAGE_SECTION_HEADER)*PeHeader.FileHeader.NumberOfSections); // если есть Rich данные то и их тоже скопируем if(sizeof(IMAGE_DOS_HEADER) < DosHeader.e_lfanew) memcpy((void*)( retadr + sizeof(IMAGE_DOS_HEADER)), &tmp[0], DosHeader.e_lfanew- sizeof(IMAGE_DOS_HEADER)); // обработаем каждую секцию for(int i = 0; i < PeHeader.FileHeader.NumberOfSections; i++) { // установим указатель в файле на начало секции в файле SetFilePointer(_hFile, Section[i].PointerToRawData, NULL, FILE_BEGIN); // считаем всё секцию ReadFile(_hFile,(void*)(retadr + Section[i].VirtualAddress), Section[i].SizeOfRawData, &rb, NULL); } // Обработаем релоки if(!ProgressReloc(retadr)) { // если ошибка // освободим память VirtualFree((void*)retadr, 0, MEM_RELEASE); retadr = 0; } // обработаем импорт else if(!ProgressImport(retadr)) { // если ошибка // освободим память VirtualFree((void*)retadr, 0, MEM_RELEASE); retadr = 0; }//* else { __asm { mov eax, PeHeader.OptionalHeader.AddressOfEntryPoint add eax, retadr // EAX = ENTRY POINT push 0 // _stdcall-> Arg3,Arg2,Arg1; ret n; push DLL_PROCESS_ATTACH // ставим флаг что подгрузили DLL push retadr call eax // передадим управление на точку входа в DLL } } } } } CloseHandle(_hFile); } return (HMODULE)retadr; Помоему он полностью повторяет исх. файл, т.е. образ в памяти ничем не будет отличаться от файла. или нет ?
Может ошибаюсь, но думаю если грузить сразу весь файл антивирусу будет легче осмыслить факт загрузки DLL.
каждая секция должна быть выравнена на размер страницы... нет никакой разницы... есть много факторов по которым можно определить, что этот блок памяти является dll...
// Обработаем релоки if(!ProgressReloc(retadr)) // обработаем импорт else if(!ProgressImport(retadr)) такчто не совсем
RDMess Антивирус дело десятое. Главный вопрос можно ли обойтись без этих манипуляций с секциями. Но получить абсолютно рабочий образ.
я думаю только в одном случае... когда выравнивание секций в файле совпадает с выравниванием виртуальной памяти...
Clyde Даг вот! это уже финиш, т.е. все необходимые данные к этому моменту УЖЕ скопированы в память. Собссно вопрос то в том, можно ли копировать не по частям а полностью как есть. Дальше чисто работа с данными в памяти, меняем относительные адреса на абсолютные, НО образ то уже не трогается.
Rel Но в функции загрузки выравнивание не упоминается. Первый раз оно встречается в релоках Код (Text): ULONG ProgressReloc(ULONG filebase) { ULONG PE; ULONG IB; ULONG cnt; ULONG x; ULONG Delta; PFIXREC fixrec; USHORT fixtype; USHORT fixoffset; PE = *(ULONG *)(filebase + 0x3C) + filebase;// получаем адрес PE заголовка IB = *(ULONG *)(PE + 0x34); // IMAGE BASE if(filebase == IB) return 1; // Если совпадает с загруженным адресом, то фиксить не нужно ничего Delta = filebase - IB; // выцесляем дельта смещение. if(!*(ULONG *)(PE + 0xA0))return 1; // если нет релоков то выходим fixrec = (PFIXREC)(*(ULONG *)(PE + 0xA0) + filebase); // получаем адрес таблицы релоков while(fixrec->BlockSize) // если таблица не пуста { cnt = (fixrec->BlockSize - 8) >> 1; // вычеслим кол-во элементов for(x = 0; x < cnt; x++) { fixtype = (fixrec->TOR[x]) >> 12; // типа фиксации fixoffset = (fixrec->TOR[x]) % 4096; // офсет внутри 4-х килобайтового блока if(!fixtype) continue; // если 0, то фиксация не нужна if(fixtype == 3) // если 3, то прибавить дельта смещение { *(ULONG*)(filebase + fixoffset + fixrec->PageRVA) = *(ULONG*)(filebase + fixoffset + fixrec->PageRVA) + Delta; } else return 0; // все остальные случае вызовут ошибку (хотя их и не будет теоретически) } fixrec = (PFIXREC)((ULONG) fixrec + fixrec->BlockSize);// следующая таблица релоков } return 1; }
вас носом ткнуть? PointerToRawData - смещение секции от начала файла VirtualAddress - смещение секции от базового адреса загрузки в виртуальной памяти с учетом выравнивания PointerToRawData != VirtualAddress кроме наверное одной исключительной ситуации, когда: и вообще говоря код в этой статье далеко не лучший из тех, что можно найти на просторах интернета... и еще более вообще говоря, вам нужно подробнее прочесть про формат PE-файлов и работу виндового загрузщика... релоки - как раз одна из причин, по которой файл не будет работать без должного выравнивания... если вы грузите файл без выравнивания, то необходимо фиксить релоки должным образом...
вообще если подумать о загрузке без выравнивания - задача интересная... тут надо хорошо знать как генерирует код компилятор, что собирает dll... впринципе это можно довольно просто сделать, если запихнуть весь код и данные в одну секцию, все переходы сделать относительными (позишн индепендент код)... я к тому, что если мы грузим нашу длл или экзе-файл, которую сами собираем - это возможно... если мы грузим нормальные бинарники, то ни о какой стабильности этого подхода не может быть и речи... много подводных камней...
qwe8013 Это уже понятно. Может разовьете идею Rel'a, насчёт, объединить все секции в одну ? У меня тока с .reloc такой фокус не проходит #pragma comment(linker,"/MERGE:.rdata=.text") #pragma comment(linker,"/MERGE:.data=.text")
тут нужен асм... на сях сложно такое сделать... релоки ты не запихнешь в одну секцию, их можно отключить... но тогда нужно, чтобы все переходы, привязки к данным и тд в коде были относительными... как сишный компилятор заставить это делать, я не знаю... нужен пи-код (позишн индепендент), его наверное только на асме можно сделать... рекомендую yasm, он простой, легко интегрируется в твою любимую визуал студию, линковать можно студийным линкером, можно миксить x86 и x64 код в одном obj-файле...
SZ Вы конечно можете создать только одну секцию, но загрузить весь файл разом всё-равно не получится ,из-за выравнивания. PS Гораздо проще загрузить файл с выравниваниями чем писать пи-код.
Это да, хотя при определённых условиях написать пи-код достаточно легко. Например, fasm при генерации 64-битного кода генерирует пи-код. Главное использовать lea rax, [address] вместо mov rax, address. Поскольку в таком случае вся адресация идёт относительно указателя команд. В 32-битном режиме, разумеется, такой фокус не пройдёт (EIP не доступен напрямую для чтения), поэтому придётся повозится. Только вот вообще зачем грузить файл без выравнивания? Только разве что из спортивного интереса. Других случаев, когда целый PE файл надо так грузить я не вижу.
1. Сократить кол-во вызовов ReadFile с (4 + кол-во секций) до 1. 2. Сделать загрузчик комактнее==быстрее. Для себя выбрал промежуточный вариант, загружаю за раз в память потом по этой копии делаю образ. убыток: двойной размер памяти.