Ручная загрузка DLL

Тема в разделе "WASM.BEGINNERS", создана пользователем SZ, 5 янв 2011.

  1. SZ

    SZ New Member

    Публикаций:
    0
    Регистрация:
    19 авг 2010
    Сообщения:
    30
    Всем привет.
    Нашёл способ тут http://www.slesh.name/?act=articles&subact=show&nid=12
    Всё получилось и работает НО
    у меня возник вопрос, зачем автор грузит дллку по частям (хидеры, секции ...) ?
    Разве нельзя весь образ разом грузануть, а потом уже по нему пройтись и заполнить структуры ?
    вот кусок с вопросом
    Код (Text):
    1. ULONG                   retadr = 0;
    2. DWORD                   rb;
    3. HANDLE                  _hFile;
    4. IMAGE_DOS_HEADER        DosHeader;
    5. IMAGE_NT_HEADERS        PeHeader;
    6. IMAGE_SECTION_HEADER    Section[10];
    7. char                    tmp[1024];
    8.  
    9. _hFile = CreateFile(_filename,                  // открываемый файл
    10.                     GENERIC_READ|GENERIC_WRITE, // открываем для чтения/записи
    11.                     0,                          // совместно не используется
    12.                     NULL,                       // защита по умолчанию
    13.                     OPEN_EXISTING,              // только существующий файл
    14.                     FILE_ATTRIBUTE_NORMAL,      // обычный файл
    15.                     NULL);                      // атрибутов шаблона нет
    16. if(_hFile==INVALID_HANDLE_VALUE)_hFile=0;
    17.  
    18.     if(_hFile)
    19.     {
    20.     // Читаем DOS заголовок
    21.     ReadFile(_hFile, &DosHeader, sizeof(IMAGE_DOS_HEADER), &rb, NULL);
    22.         if(DosHeader.e_magic == IMAGE_DOS_SIGNATURE)    // проверим DOS сигнатуру
    23.         {
    24.             // если есть какимето данные между DOS заголовком и PE
    25.             // то считаем их. В MS компиляторах это часто Rich данные
    26.             if(sizeof(IMAGE_DOS_HEADER) < DosHeader.e_lfanew)
    27.             {
    28.             ReadFile(_hFile, &tmp[0], DosHeader.e_lfanew - sizeof(IMAGE_DOS_HEADER), &rb, 0);
    29.             }
    30.         // установим указатель в файле на PE заголовок
    31.         SetFilePointer(_hFile, DosHeader.e_lfanew, NULL, FILE_BEGIN);
    32.         // Читаем PE заголовок
    33.         ReadFile(_hFile, &PeHeader, sizeof(IMAGE_NT_HEADERS), &rb, NULL);
    34.             if(PeHeader.Signature == IMAGE_NT_SIGNATURE)// проверим PE сигнатуру
    35.             {
    36.             // считаем 10 секций
    37.             ReadFile(   _hFile,
    38.                         &Section[0],
    39.                         sizeof(IMAGE_SECTION_HEADER) * PeHeader.FileHeader.NumberOfSections,
    40.                         &rb,
    41.                         NULL);
    42.             // выделим памяти столько, сколько указано в SIZE OF BASE
    43.             retadr = (ULONG)VirtualAlloc(   0,
    44.                                             PeHeader.OptionalHeader.SizeOfImage,
    45.                                             MEM_COMMIT|MEM_RESERVE|MEM_TOP_DOWN,
    46.                                             PAGE_EXECUTE_READWRITE);
    47.                 if(retadr)  // если память выделилась
    48.                 {
    49.                 // скопируем туда DOS заголовок
    50.                 memcpy((void *)retadr, &DosHeader, sizeof(IMAGE_DOS_HEADER));
    51.                 // скопируем туда PE заголовок
    52.                 memcpy((void *)(retadr + DosHeader.e_lfanew),
    53.                                 &PeHeader,
    54.                                 sizeof(IMAGE_NT_HEADERS));
    55.                 // скопируем туда таблицу секций
    56.                 memcpy((void *)(retadr + DosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS)),
    57.                                 &Section[0],
    58.                                 sizeof(IMAGE_SECTION_HEADER)*PeHeader.FileHeader.NumberOfSections);
    59.                 // если есть Rich данные то и их тоже скопируем
    60.                     if(sizeof(IMAGE_DOS_HEADER) < DosHeader.e_lfanew)
    61.                         memcpy((void*)( retadr + sizeof(IMAGE_DOS_HEADER)),
    62.                                         &tmp[0],
    63.                                         DosHeader.e_lfanew- sizeof(IMAGE_DOS_HEADER));
    64.                     // обработаем каждую секцию
    65.                     for(int i = 0; i < PeHeader.FileHeader.NumberOfSections; i++)
    66.                     {
    67.                     // установим указатель в файле на начало секции в файле
    68.                     SetFilePointer(_hFile, Section[i].PointerToRawData, NULL, FILE_BEGIN);
    69.                     // считаем всё секцию
    70.                     ReadFile(_hFile,(void*)(retadr + Section[i].VirtualAddress),
    71.                                             Section[i].SizeOfRawData,
    72.                                             &rb,
    73.                                             NULL);
    74.                     }
    75.                     // Обработаем релоки
    76.                     if(!ProgressReloc(retadr))
    77.                     {
    78.                         //  если ошибка
    79.                         //  освободим память
    80.                         VirtualFree((void*)retadr, 0, MEM_RELEASE);
    81.                         retadr = 0;
    82.                     }       // обработаем импорт
    83.                     else    if(!ProgressImport(retadr))
    84.                             {
    85.                                 //  если ошибка
    86.                                 //  освободим память
    87.                             VirtualFree((void*)retadr, 0, MEM_RELEASE);
    88.                             retadr = 0;
    89.                             }//*
    90.                             else
    91.                             {
    92.                                 __asm
    93.                                 {
    94.                                 mov eax, PeHeader.OptionalHeader.AddressOfEntryPoint
    95.                                 add eax, retadr         // EAX = ENTRY POINT
    96.                                 push 0                  // _stdcall-> Arg3,Arg2,Arg1; ret n;
    97.                                 push DLL_PROCESS_ATTACH // ставим флаг что подгрузили DLL
    98.                                 push retadr
    99.                                 call eax    // передадим управление на точку входа в DLL
    100.                                 }
    101.                             }
    102.                 }
    103.             }
    104.         }
    105.     CloseHandle(_hFile);
    106.     }
    107. return (HMODULE)retadr;
    Помоему он полностью повторяет исх. файл, т.е. образ в памяти ничем не будет отличаться от файла.
    или нет ?
     
  2. RDMess

    RDMess Member

    Публикаций:
    0
    Регистрация:
    28 дек 2009
    Сообщения:
    51
    Может ошибаюсь, но думаю если грузить сразу весь файл антивирусу будет легче осмыслить факт загрузки DLL.
     
  3. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    каждая секция должна быть выравнена на размер страницы...

    нет никакой разницы... есть много факторов по которым можно определить, что этот блок памяти является dll...
     
  4. Clyde

    Clyde New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2009
    Сообщения:
    154
    // Обработаем релоки
    if(!ProgressReloc(retadr))
    // обработаем импорт
    else if(!ProgressImport(retadr))

    такчто не совсем :)
     
  5. SZ

    SZ New Member

    Публикаций:
    0
    Регистрация:
    19 авг 2010
    Сообщения:
    30
    RDMess
    Антивирус дело десятое.
    Главный вопрос можно ли обойтись без этих манипуляций с секциями.
    Но получить абсолютно рабочий образ.
     
  6. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    я думаю только в одном случае... когда выравнивание секций в файле совпадает с выравниванием виртуальной памяти...
     
  7. SZ

    SZ New Member

    Публикаций:
    0
    Регистрация:
    19 авг 2010
    Сообщения:
    30
    Clyde
    Даг вот!
    это уже финиш, т.е. все необходимые данные к этому моменту УЖЕ скопированы в память.
    Собссно вопрос то в том, можно ли копировать не по частям а полностью как есть.
    Дальше чисто работа с данными в памяти, меняем относительные адреса на абсолютные, НО образ то уже не трогается.
     
  8. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
  9. SZ

    SZ New Member

    Публикаций:
    0
    Регистрация:
    19 авг 2010
    Сообщения:
    30
    Rel
    Но в функции загрузки выравнивание не упоминается.
    Первый раз оно встречается в релоках
    Код (Text):
    1. ULONG ProgressReloc(ULONG filebase)
    2. {
    3. ULONG       PE;
    4. ULONG       IB;
    5. ULONG       cnt;
    6. ULONG       x;
    7. ULONG       Delta;
    8. PFIXREC     fixrec;
    9. USHORT      fixtype;
    10. USHORT      fixoffset;
    11.  
    12. PE = *(ULONG *)(filebase + 0x3C) + filebase;// получаем адрес PE заголовка
    13. IB = *(ULONG *)(PE + 0x34);                 // IMAGE BASE
    14. if(filebase == IB) return 1;                // Если совпадает с загруженным адресом, то фиксить не нужно ничего
    15. Delta = filebase - IB;                      // выцесляем дельта смещение.
    16. if(!*(ULONG *)(PE + 0xA0))return 1;         // если нет релоков то выходим
    17. fixrec = (PFIXREC)(*(ULONG *)(PE + 0xA0) + filebase); // получаем адрес таблицы релоков
    18.     while(fixrec->BlockSize)        // если таблица не пуста
    19.     {
    20.     cnt = (fixrec->BlockSize - 8) >> 1; // вычеслим кол-во элементов
    21.         for(x = 0; x < cnt; x++)
    22.         {
    23.         fixtype = (fixrec->TOR[x]) >> 12;       // типа фиксации
    24.         fixoffset = (fixrec->TOR[x]) % 4096;    // офсет внутри 4-х килобайтового блока
    25.         if(!fixtype) continue;        // если 0, то фиксация не нужна
    26.             if(fixtype == 3)          // если 3, то прибавить дельта смещение
    27.             {
    28.             *(ULONG*)(filebase + fixoffset + fixrec->PageRVA) = *(ULONG*)(filebase + fixoffset + fixrec->PageRVA) + Delta;
    29.             }
    30.             else return 0;  // все остальные случае вызовут ошибку (хотя их и не будет теоретически)
    31.         }
    32.     fixrec = (PFIXREC)((ULONG) fixrec + fixrec->BlockSize);// следующая таблица релоков
    33.     }
    34. return 1;
    35. }
     
  10. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    вас носом ткнуть?

    PointerToRawData - смещение секции от начала файла
    VirtualAddress - смещение секции от базового адреса загрузки в виртуальной памяти с учетом выравнивания
    PointerToRawData != VirtualAddress кроме наверное одной исключительной ситуации, когда:
    и вообще говоря код в этой статье далеко не лучший из тех, что можно найти на просторах интернета... и еще более вообще говоря, вам нужно подробнее прочесть про формат PE-файлов и работу виндового загрузщика...

    релоки - как раз одна из причин, по которой файл не будет работать без должного выравнивания... если вы грузите файл без выравнивания, то необходимо фиксить релоки должным образом...
     
  11. SZ

    SZ New Member

    Публикаций:
    0
    Регистрация:
    19 авг 2010
    Сообщения:
    30
    Терь всё ясно )
    Спасибо Rel
     
  12. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    вообще если подумать о загрузке без выравнивания - задача интересная... тут надо хорошо знать как генерирует код компилятор, что собирает dll... впринципе это можно довольно просто сделать, если запихнуть весь код и данные в одну секцию, все переходы сделать относительными (позишн индепендент код)... я к тому, что если мы грузим нашу длл или экзе-файл, которую сами собираем - это возможно... если мы грузим нормальные бинарники, то ни о какой стабильности этого подхода не может быть и речи... много подводных камней...
     
  13. qwe8013

    qwe8013 New Member

    Публикаций:
    0
    Регистрация:
    28 май 2009
    Сообщения:
    198
    SZ
    Потому что PointerToRawData и VirtualAddress у секций могут быть разными.
     
  14. SZ

    SZ New Member

    Публикаций:
    0
    Регистрация:
    19 авг 2010
    Сообщения:
    30
    qwe8013
    Это уже понятно.
    Может разовьете идею Rel'a, насчёт, объединить все секции в одну ?

    У меня тока с .reloc такой фокус не проходит
    #pragma comment(linker,"/MERGE:.rdata=.text")
    #pragma comment(linker,"/MERGE:.data=.text")
     
  15. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    тут нужен асм... на сях сложно такое сделать... релоки ты не запихнешь в одну секцию, их можно отключить... но тогда нужно, чтобы все переходы, привязки к данным и тд в коде были относительными... как сишный компилятор заставить это делать, я не знаю... нужен пи-код (позишн индепендент), его наверное только на асме можно сделать... рекомендую yasm, он простой, легко интегрируется в твою любимую визуал студию, линковать можно студийным линкером, можно миксить x86 и x64 код в одном obj-файле...
     
  16. qwe8013

    qwe8013 New Member

    Публикаций:
    0
    Регистрация:
    28 май 2009
    Сообщения:
    198
    SZ
    Вы конечно можете создать только одну секцию, но загрузить весь файл разом всё-равно не получится ,из-за выравнивания.

    PS Гораздо проще загрузить файл с выравниваниями чем писать пи-код.
     
  17. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    согласен с этим...
     
  18. KIV

    KIV Member

    Публикаций:
    0
    Регистрация:
    16 июл 2009
    Сообщения:
    231
    Это да, хотя при определённых условиях написать пи-код достаточно легко.
    Например, fasm при генерации 64-битного кода генерирует пи-код. Главное использовать lea rax, [address] вместо mov rax, address. Поскольку в таком случае вся адресация идёт относительно указателя команд.
    В 32-битном режиме, разумеется, такой фокус не пройдёт (EIP не доступен напрямую для чтения), поэтому придётся повозится.

    Только вот вообще зачем грузить файл без выравнивания? Только разве что из спортивного интереса. Других случаев, когда целый PE файл надо так грузить я не вижу.
     
  19. SZ

    SZ New Member

    Публикаций:
    0
    Регистрация:
    19 авг 2010
    Сообщения:
    30
    1. Сократить кол-во вызовов ReadFile с (4 + кол-во секций) до 1.
    2. Сделать загрузчик комактнее==быстрее.
    Для себя выбрал промежуточный вариант, загружаю за раз в память потом по этой копии делаю образ.
    убыток: двойной размер памяти.
     
  20. qwe8013

    qwe8013 New Member

    Публикаций:
    0
    Регистрация:
    28 май 2009
    Сообщения:
    198
    SZ
    Ну и на сколько по вашему он будет быстрее?
    И какой смысл сокращать кол-во вызовов ReadFile?