Сейчас на форуме: (+6 невидимых)

 eXeL@B —› Протекторы —› Добавление новой секции затирает секцию кода
Посл.ответ Сообщение

Ранг: 7.5 (гость), 1thx
Активность: 0.010
Статус: Участник

Создано: 02 октября 2016 22:25
· Личное сообщение · #1

Хочу написать упаковщик. К стабу добавляется секция с упакованным имиджем и после запуска стаба этот имидж должен распаковываться в памяти и запускаться. Я взял за основу такой код добавления секции:

Code:
  1. BOOL AddSection(LPTSTR filepath, LPTSTR sectionName, DWORD sizeOfSection)
  2. {
  3.          DWORD bytesRW;
  4.          BOOL bEnoughSpace;
  5.          IMAGE_SECTION_HEADER newSection;
  6.          int i;
  7.  
  8.          HANDLE file = CreateFile(filepath,
  9.                  GENERIC_READ | GENERIC_WRITE, 0,
  10.                  NULL,
  11.                  OPEN_EXISTING,
  12.                  FILE_ATTRIBUTE_NORMAL,
  13.                  NULL);
  14.          if (file == INVALID_HANDLE_VALUE)
  15.                  return FALSE;
  16.  
  17.          DWORD fileSize = GetFileSize(file, NULL);
  18.          BYTE *pByte = VirtualAlloc(NULL, fileSize, MEM_COMMIT, PAGE_READWRITE);
  19.          ReadFile(file, pByte, fileSize, &bytesRW, NULL);
  20.  
  21.          PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)pByte;
  22.          if (dos->e_magic != IMAGE_DOS_SIGNATURE)
  23.                  return FALSE;
  24.          PIMAGE_FILE_HEADER FH = (PIMAGE_FILE_HEADER)(pByte + dos->e_lfanew + sizeof(DWORD));
  25.          PIMAGE_OPTIONAL_HEADER OH = (PIMAGE_OPTIONAL_HEADER)(pByte + dos->e_lfanew + sizeof(DWORD)+sizeof(IMAGE_FILE_HEADER));
  26.          PIMAGE_SECTION_HEADER SH = (PIMAGE_SECTION_HEADER)(pByte + dos->e_lfanew + sizeof(IMAGE_NT_HEADERS));
  27.  
  28.          for (= 0; i < sizeof(IMAGE_SECTION_HEADER); i++)
  29.          {
  30.                  if (*((char *)&SH[FH->NumberOfSections] + i) != 0)
  31.                  {
  32.                         MessageBox(NULL, "Not enough free space", "Error", MB_OK);
  33.                         bEnoughSpace = FALSE;
  34.                  }
  35.          }
  36.  
  37.          
  38.  
  39.          ZeroMemory(&SH[FH->NumberOfSections], sizeof(IMAGE_SECTION_HEADER));
  40.          CopyMemory(&SH[FH->NumberOfSections].Name, sectionName, 8);
  41.  
  42.          SH[FH->NumberOfSections].Misc.VirtualSize = align(sizeOfSection, OH->SectionAlignment, 0);
  43.     SH[FH->NumberOfSections].VirtualAddress = align(SH[FH->NumberOfSections - 1].Misc.VirtualSize, OH->SectionAlignment, SH[FH->NumberOfSections - 1].VirtualAddress);
  44.     SH[FH->NumberOfSections].SizeOfRawData = align(sizeOfSection, OH->FileAlignment, 0);
  45.     SH[FH->NumberOfSections].PointerToRawData = align(SH[FH->NumberOfSections - 1].SizeOfRawData, OH->FileAlignment, SH[FH->NumberOfSections - 1].PointerToRawData);
  46.     SH[FH->NumberOfSections].Characteristics = 0xE00000E0;
  47.          /*
  48.         0xE00000E0 = IMAGE_SCN_MEM_WRITE |
  49.                      IMAGE_SCN_CNT_CODE  |
  50.                      IMAGE_SCN_CNT_UNINITIALIZED_DATA  |
  51.                      IMAGE_SCN_MEM_EXECUTE |
  52.                      IMAGE_SCN_CNT_INITIALIZED_DATA |
  53.                      IMAGE_SCN_MEM_READ 
  54.     */
  55.          SetFilePointer(file, SH[FH->NumberOfSections].PointerToRawData + SH[FH->NumberOfSections].SizeOfRawData, NULL, FILE_BEGIN);
  56.          SetEndOfFile(file);
  57.          OH->SizeOfImage = SH[FH->NumberOfSections].VirtualAddress + SH[FH->NumberOfSections].Misc.VirtualSize;
  58.          OH->SizeOfImage = SH[FH->NumberOfSections].VirtualAddress + SH[FH->NumberOfSections].Misc.VirtualSize;
  59.          FH->NumberOfSections += 1;
  60.          SetFilePointer(file, 0, NULL, FILE_BEGIN);
  61.  
  62.          WriteFile(file, pByte, fileSize, &bytesRW, NULL);
  63.     CloseHandle(file);
  64.  
  65.          VirtualFree(pByte, 0, MEM_RELEASE);
  66.  
  67.          return TRUE;
  68. }
  69.  
  70. BOOL AddCode(char *filepath)
  71. {
  72.          DWORD bytesRW;
  73.  
  74.     HANDLE file = CreateFile(filepath,
  75.                  GENERIC_READ | GENERIC_WRITE, 0,
  76.                  NULL,
  77.                  OPEN_EXISTING,
  78.                  FILE_ATTRIBUTE_NORMAL,
  79.                  NULL);
  80.     if (file == INVALID_HANDLE_VALUE) 
  81.         return FALSE;
  82.  
  83.     DWORD fileSize = GetFileSize(file, NULL);
  84.     BYTE *pByte = VirtualAlloc(NULL, fileSize, MEM_COMMIT, PAGE_READWRITE);
  85.  
  86.     ReadFile(file, pByte, fileSize, &bytesRW, NULL);
  87.  
  88.     PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)pByte;
  89.     PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(pByte + dos->e_lfanew);
  90.  
  91.     //since we added a new section,it must be the last section added,cause of the code inside
  92.     //AddSection function,thus we must get to the last section to insert our secret data :)
  93.     PIMAGE_SECTION_HEADER first = IMAGE_FIRST_SECTION(nt);
  94.     PIMAGE_SECTION_HEADER last = first + (nt->FileHeader.NumberOfSections - 1);
  95.  
  96.     SetFilePointer(file, last->PointerToRawData, NULL, FILE_BEGIN);
  97.          ShowNum(last->PointerToRawData);
  98.     char *str = "NEW SECTION";
  99.     WriteFile(file, str, strlen(str), &bytesRW, 0);
  100.     CloseHandle(file);
  101.  
  102.          VirtualFree(pByte, 0, MEM_RELEASE);
  103.  
  104.     return TRUE;
  105. }



Проблема в том, что код рассчитан на PE-файлы, в которых между таблицей секций и первой секцией есть пустое пространство для нового заголовка. У меня линкеры создают файлы, в которых таблица секций лежит вплотную к секции кода, поэтому при добавлении нового заголовка секция кода затирается.

Почему кто-то рассчитывал на то, что будет куда вставлять новый заголовок? Почему пустого места нет?

Я решил перекраивать дисковый имидж и пишу такой код:

Code:
  1. BOOL AddSection(LPTSTR filepath, LPTSTR sectionName, DWORD sizeOfSection)
  2. {
  3.          DWORD bytesRW;
  4.          IMAGE_SECTION_HEADER newSection;
  5.  
  6.          HANDLE file = CreateFile(filepath,
  7.                  GENERIC_READ | GENERIC_WRITE, 0,
  8.                  NULL,
  9.                  OPEN_EXISTING,
  10.                  FILE_ATTRIBUTE_NORMAL,
  11.                  NULL);
  12.          if (file == INVALID_HANDLE_VALUE)
  13.                  return FALSE;
  14.  
  15.          DWORD fileSize = GetFileSize(file, NULL);
  16.          BYTE *pByte = VirtualAlloc(NULL, fileSize, MEM_COMMIT, PAGE_READWRITE);
  17.          ReadFile(file, pByte, fileSize, &bytesRW, NULL);
  18.  
  19.          PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)pByte;
  20.          if (dos->e_magic != IMAGE_DOS_SIGNATURE)
  21.                  return FALSE;
  22.          PIMAGE_FILE_HEADER FH = (PIMAGE_FILE_HEADER)(pByte + dos->e_lfanew + sizeof(DWORD));
  23.          PIMAGE_OPTIONAL_HEADER OH = (PIMAGE_OPTIONAL_HEADER)(pByte + dos->e_lfanew + sizeof(DWORD)+sizeof(IMAGE_FILE_HEADER));
  24.          PIMAGE_SECTION_HEADER SH = (PIMAGE_SECTION_HEADER)(pByte + dos->e_lfanew + sizeof(IMAGE_NT_HEADERS));
  25.  
  26.          // Настроить параметры новой секции
  27.          ZeroMemory(&newSection, sizeof(IMAGE_SECTION_HEADER));
  28.          CopyMemory(&newSection.Name, sectionName, 8);
  29.          newSection.Misc.VirtualSize = align(sizeOfSection, OH->SectionAlignment, 0);
  30.          newSection.VirtualAddress = align(SH[FH->NumberOfSections - 1].Misc.VirtualSize,
  31.                                                OH->SectionAlignment,
  32.                                                SH[FH->NumberOfSections - 1].VirtualAddress);
  33.          newSection.SizeOfRawData = align(sizeOfSection, OH->FileAlignment, 0);
  34.          newSection.PointerToRawData = align(SH[FH->NumberOfSections - 1].SizeOfRawData,
  35.                                                       OH->FileAlignment,
  36.                                                       SH[FH->NumberOfSections - 1].PointerToRawData);
  37.          newSection.Characteristics = 0xE00000E0;
  38.          //
  39.     //    0xE00000E0 = IMAGE_SCN_MEM_WRITE |
  40.     //                 IMAGE_SCN_CNT_CODE  |
  41.     //                 IMAGE_SCN_CNT_UNINITIALIZED_DATA  |
  42.     //                 IMAGE_SCN_MEM_EXECUTE |
  43.     //                 IMAGE_SCN_CNT_INITIALIZED_DATA |
  44.     //                 IMAGE_SCN_MEM_READ 
  45.     
  46.          OH->SizeOfImage = newSection.VirtualAddress + newSection.Misc.VirtualSize;
  47.          FH->NumberOfSections += 1;
  48.          SetFilePointer(file, 0, NULL, FILE_BEGIN);
  49.  
  50.          // Записать все до последнего заголовка секций
  51.          WriteFile(file,
  52.                  pByte,
  53.                  dos->e_lfanew + 4 +
  54.                         sizeof(IMAGE_FILE_HEADER) +
  55.                         FH->SizeOfOptionalHeader +
  56.                         (FH->NumberOfSections - 1) * sizeof(IMAGE_SECTION_HEADER),
  57.                  &bytesRW,
  58.                  NULL);
  59.          // Записать заголовок новой секции
  60.          WriteFile(file, &newSection, sizeof(IMAGE_SECTION_HEADER), &bytesRW, NULL);
  61.          // Записать все, начиная с секции кода
  62.          WriteFile(file, (pByte + GetFilePointer(file)), fileSize - GetFilePointer(file), &bytesRW, NULL);
  63.          
  64.     CloseHandle(file);
  65.  
  66.          VirtualFree(pByte, 0, MEM_RELEASE);
  67.  
  68.          return TRUE;
  69. }


Алгоритм такой:
1. Читаю дсковый имидж в буфер
2. На стеке создаю и заполняю хедер новой секции
3. В optional header'е увеличиваю размер имиджа, в file header'е увеличиваю количество секций
4. Записываю обратно в файл часть буфера до таблицы секций включительно
5. Записываю созданный заголовок секции
5. Записываю все, что лежало после таблицы секций: то есть секцию кода и остальное

Примеры программ с добавленной секцией:

Оригинальный способ, затирающий секцию кода: http://rgho.st/6R8Hycl6s Видно, что после записи строки "NEW SECTION" секция выравнивается нулями

Мой вариант: http://rgho.st/7wgBqhJSf почему-то секция нулями не выравнивается

В обеих случаях файл не запускается. Что я делаю не так? Какие поля надо пересчитать после добавления заголовка секции и сдвигания секции кода вниз?




Ранг: 52.0 (постоянный), 146thx
Активность: 0.030.08
Статус: Участник

Создано: 02 октября 2016 23:44 · Поправил: UniSoft
· Личное сообщение · #2

ZeroMemory пишет:
Хочу написать упаковщик. ...этот имидж должен распаковываться в памяти и запускаться.

А вы в курсе, что кроме распаковки нужно еще обработать таблицу релоков, таблицу импорта, TLS Callbacks (если имеется), ...?

ZeroMemory пишет:
файл не запускается. Что я делаю не так?

нельзя просто так сдвинуть данные на размер секции!
а как же выравнивание? А PointerToRawData всех остальных секций?

Изучайте формат PE...
https://habrahabr.ru/company/xakep/blog/139138/
https://vxheaven.org/0x48k/etc/peload_dll.cpp


-=AkaBOSS=--=AkaBOSS=- пишет:
RVA?

Ну да, не правильно выразился... бывает
конечно же имелось в виду PointerToRawData




Ранг: 150.3 (ветеран), 175thx
Активность: 0.160.07
Статус: Участник

Создано: 03 октября 2016 01:45 · Поправил: -=AkaBOSS=-
· Личное сообщение · #3

чтоб было проще понять - рассматривайте PEхидер как еще одну секцию.
нужно сделать примерно так:
1. РазмерА = общий размер пехидера и заголовков секций
2. РазмерБ = РазмерА, округлённый вверх до FileAlignment - это фактическое количество данных, которые будут считаны из файла в память
3. РазмерВ = (РазмерА+sizeof (IMAGE_SECTION_HEADER)), округляем вверх до FileAlignment - это размер хидера который будет после добавления секции
4. если РазмерБ и РазмерВ не равны, значит места не хватит и надо двигать секции.
5. соответственно, нужно пройтись по заголовкам всех секций и прибавить к ним разницу между РазмеромВ и РазмеромБ
6. разумеется, надо и сами секции физически сдвинуть в файле

ZeroMemory пишет:
Code:
  1. newSection.PointerToRawData = align(SH[FH->NumberOfSections - 1].SizeOfRawData,  OH->FileAlignment, SH[FH->NumberOfSections - 1].PointerToRawData);

если уж говорить о возможных багах, то это - их потенциальный источник.
порядок секций в пехидере гарантированно отвечает за порядок, в котором они будут располагаться в памяти.
Но! Секция, расположенная в хидере последней, не обязательно будет последней в файле) В файле они могут располагаться в любом порядке.
К тому же, секция может иметь виртуальное представление, но не иметь физического (проще говоря - PointerToRawData и SizeOfRawData могут быть нулевыми)
семпл в аттаче
2209_03.10.2016_EXELAB.rU.tgz - test.exe



UniSoft пишет:
А RVA всех остальных секций?

RVA?



Ранг: 431.7 (мудрец), 389thx
Активность: 0.730.32
Статус: Участник

Создано: 03 октября 2016 02:12 · Поправил: dosprog
· Личное сообщение · #4

VA секции кода чаще всего .401000 при базовом адресе .400000.
1000h байтов это 4кб - можно туда слона запихнуть, а не то, что ещё один дескриптор секции.
Но перекомпоновать файл понадобится серьёзно.


--Добавлено--

ZeroMemory пишет:
Хочу написать упаковщик.

Может, лучше ну его на? - UPX вполне справляется.
Если уж сильное, стойкое и непреодолимое желание рулить,
то можно помучить сорсы UPX'а






Ранг: 337.5 (мудрец), 348thx
Активность: 2.112.42
Статус: Участник

Создано: 03 октября 2016 10:45 · Поправил: difexacaw
· Личное сообщение · #5

Норм решение это полноценный инфект, тоесть нужно интегрироваться в код, а предыдущий и прочий сохранить в конец файла. Иначе по ровному никак не сделать, ну или не сделать проще, учитывая что у тс модуля формата "минимальный исполняемый файл".

-----
vx





Ранг: 110.8 (ветеран), 104thx
Активность: 0.090.01
Статус: Участник

Создано: 03 октября 2016 10:57
· Личное сообщение · #6

Ну кто вам вообще сказал, что PE-хидер должен идти следом за дос-хидером ?.. У структуры IMAGE_DOS_HEADER есть замечательное поле - e_lfanew. А если так, то почему бы, например, не разместить PE-хидер где-нибудь в хвосте ?

P.S. Мыслим шире




Ранг: 337.5 (мудрец), 348thx
Активность: 2.112.42
Статус: Участник

Создано: 03 октября 2016 10:59 · Поправил: difexacaw
· Личное сообщение · #7

Rainbow

Потому, что такой формат сразу выпилят аверы. Модуль будет запускаться, но станет не рабочим в ав окружении(система уже давно не только загрузчик). Мыслим шире

-----
vx


| Сообщение посчитали полезным: dosprog


Ранг: 110.8 (ветеран), 104thx
Активность: 0.090.01
Статус: Участник

Создано: 03 октября 2016 11:03
· Личное сообщение · #8

Похоже у кого-то эвристика зашкалила

Добавлено спустя 15 минут
Видимо мне действительно не следовало влезать в эти дела, когда трукодеры с аверными движками сражаются, не совладав с pe-хидером.. I'm sorry




Ранг: 337.5 (мудрец), 348thx
Активность: 2.112.42
Статус: Участник

Создано: 03 октября 2016 12:12 · Поправил: difexacaw
· Личное сообщение · #9

Rainbow

Вероятно так. На данный момент проблема формата не разрешима, так как критерий детекта основан на выборке. Ав интегрирован в ось и в лучшем случае он один, наверно вы далеки от этой темы. Такой изврат с форматом не приемлем. Были времена, очень давно когда в ядерном лодере(который мапит образ) было куча багов, это работало, но тогда не было столько ав. Сейчас же проблема не пропатчить модуль, а выполнить это так, чтобы не было реакции ав.

-----
vx




Ранг: 7.5 (гость), 1thx
Активность: 0.010
Статус: Участник

Создано: 03 октября 2016 20:18
· Личное сообщение · #10

UniSoft пишет:
А вы в курсе, что кроме распаковки нужно еще обработать таблицу релоков, таблицу импорта, TLS Callbacks (если имеется), ...?

Да, я знаю, что придется частично реализовать PE-загрузчик, но мне это интересно, хочется разобраться. Я делал инфект в code cave'ы и думал, что уж добавление секции будет проще, но оказалось, что все плохо, и старые способы добавления секции почему-то перестали работать.

Вот такой у меня исходный файл, над которым я издеваюсь http://rgho.st/8VcqfWmrR




Ранг: 52.0 (постоянный), 146thx
Активность: 0.030.08
Статус: Участник

Создано: 03 октября 2016 21:02 · Поправил: UniSoft
· Личное сообщение · #11

DEL



Ранг: 64.9 (постоянный), 47thx
Активность: 0.120.02
Статус: Участник

Создано: 03 октября 2016 21:11
· Личное сообщение · #12

Пишем упаковщик PE-файлов по шагам. Шаг первый.
https://kaimi.io/2012/09/pe-packer-step-by-step-1/

так для общей инфы




Ранг: 52.0 (постоянный), 146thx
Активность: 0.030.08
Статус: Участник

Создано: 03 октября 2016 23:01 · Поправил: UniSoft
· Личное сообщение · #13

Rainbow пишет:
PE-хидер где-нибудь в хвосте ?


difexacaw пишет:
Потому, что такой формат сразу выпилят аверы.


так, не спорьте...
помыслил шире, и попробовал такой финт, ради "научного" экскремента...
результат таков, что винда финт не оценила и обозвала такой файл инвалидом... (может я чего пропустил?!)
Все же работает такой финт... PE хидер в конце файла... (пример в аттаче)
ну а аверы:
https://www.virustotal.com/en/file/d7561462254197ea01664582d453f948883c3c61af664c14cb2c5d1e8abfe712/analysis/1475524456/
хотя многим из них и в исходном exe видится угроза (ну и отупели обленились же современные аверы):
https://www.virustotal.com/en/file/8b13ab5929596517a9078127e5ea5e3c3c037acdaeee31f7f95e270c905e143d/analysis/1475524927/

96e0_04.10.2016_EXELAB.rU.tgz - testPE.rar




Ранг: 56.2 (постоянный), 14thx
Активность: 0.120
Статус: Участник

Создано: 05 октября 2016 22:03
· Личное сообщение · #14

да вт жжёт https://www.virustotal.com/ru/file/878a7f472f83765ed2781799f9062f8f81a0fa291d282ef8c6fe58756b0ccafd/analysis/1475693342/


 eXeL@B —› Протекторы —› Добавление новой секции затирает секцию кода
:: Ваш ответ
Жирный  Курсив  Подчеркнутый  Перечеркнутый  {mpf5}  Код  Вставить ссылку 
:s1: :s2: :s3: :s4: :s5: :s6: :s7: :s8: :s9: :s10: :s11: :s12: :s13: :s14: :s15: :s16:


Максимальный размер аттача: 500KB.
Ваш логин: german1505 » Выход » ЛС
   Для печати Для печати