Форум АНТИЧАТ

Форум АНТИЧАТ (https://forum.antichat.xyz/index.php)
-   Статьи (https://forum.antichat.xyz/forumdisplay.php?f=30)
-   -   Пишем простейший джойнер файлов (https://forum.antichat.xyz/showthread.php?t=160728)

becensed 02.12.2009 23:53

Пишем простейший джойнер файлов
 
Пишем простейший джойнер файлов. Часть 1.

Код:

Эта статья разбита на две части: 1. написание лоадера,
                                2. написание билдера.

   
Хочу сказать, что статья написана для _новичков_.
Люди знающие здесь ничего нового не узнают.


          I. ИНСТРУМЕНТЫ.

    - FASM (www.flatassembler.net);
    - немного терпения.


            II. ЛОАДЕР. Что это, принцип работы нашего лоадера.

  В нашем случае, под лоадером понимается .exe файл, состоящий из
распаковщика и данных, которые дописываются в конец файла. При
запуске, распаковщик считывает эти данные и делает необходимые
действия (сохранить на диск, запустить и т.д.).
    Для того, чтобы распаковщик знал, что делать с данными, они должны
быть как-то структурированы. Мы пишем простейший джойнер, поэтому с
заголовком мудрить не будем. Всё сделаем по минимуму.
    Что нам надо? Имя файла, длина имени файла, размер данных, и, допустим,
параметры командной строки и её длина. Схематично, это можно изобразить так:

                .-------------------,
                ¦ код  распаковщика ¦
                |-------------------|
                ¦  заголовок 1      ¦
                ¦  данные 1        ¦
                |-------------------|
                ¦  заголовок 2      ¦
                ¦  данные 2        ¦
                |-------------------|
                ¦  заголовок N      ¦
                ¦  данные N        ¦
                `-------------------'
              Схема 1. Простейший лоадер.

    Но как распаковщик узнает, где он заканчивается, а где начинаются данные?
Очень просто. В самом распаковщике мы создадим константу и назовем ее,
например, ldr_size. Ей присвоим значение, равное размеру распаковщика.
Откуда мы узнаем размер распаковщика? Самый простой способ:

    1. Собираем наш лоадер;
    2. смотрим его размер;
    3. присваиваем ldr_size размер;
    4. пересобираем.
       
Таким образом, принцип работы нашего лоадера следующий:
    а) открываем себя для чтения;
    б) смещаемся на ldr_size байт;
    в) обрабатываем заголовок;
    г) обрабатываем данные (создаём (скрытый) и запускаем файл);
    д) считываем следующий заголовок;
    е) если его нет, то выходим, иначе пункт в).


            III. НАПИСАНИЕ ЛОАДЕРА.

    Ну что же, с теоретической частью закончили. Можно приступить к практике.
Писать сам лоадер мы будем на ассемблере, используя fasm. Ниже приведён
полный исходный код. Я его немного прокомментировал, чтобы новичку было
легче разобраться.
    Как вы увидите, ничего сложного здесь нет. Читайте комментарии, смотрите
документацию, погоняйте в отладчике. Я не исключаю, что где-то мог допустить
ошибку. Значит вам её и исправлять
.

Код:

;-------------------------------------------------------------------------------

        format PE GUI 4.0

;-------------------------------------------------------------------------------

        include 'win32ax.inc'

;-------------------------------------------------------------------------------

        section '.data' data readable writeable

    lnFileName      dw      0  ; word для длины имени файла и
    lnCmdLine      dw      0  ; командной строки хватит вполне.
    lnFileSize      dd      0  ; длина файла (данных)

    szFileName      rb      100h    ; резервируем 100h байт для имени файла и
    szCmdLine      rb      100h    ; командной строки.

    szModule        rb      100h    ; путь нашего модуля

    ldr_size        dd      0  ; здесь будет размер лоадера, после ассемблирования

    hModule        dd      0  ; Хендл файла-лоадера
    hFiles          dd      0  ; Хендл созданных файлов (данных)
    hAllocMem      dd      0  ; Для резервирования памяти (для файлов)
    nBytesRead      dd      0  ; Количество прочитанных байт

;-------------------------------------------------------------------------------

        section '.code' code readable writeable executable

entry $
            ; Получим путь к нашему файлу
            invoke  GetModuleFileName, NULL, szModule, 100h
   
            ; Открываем наш файл для чтения/записи
            invoke  CreateFile, szModule, GENERIC_READ, \
                                FILE_SHARE_READ or FILE_SHARE_WRITE, \
                                NULL, \
                                OPEN_EXISTING, \
                                0, 0
            cmp    eax, -1    ; если не открылся
            jz      .close      ; то выйдем
            mov    [hModule], eax  ; сохраним хендл открытого файла

            ;----------------------------------------------,
            ; 2560 я получил после первого ассемблирования |
            ; Например, упаковав лоадер с помощью upack,  |
            ; я получил размер 1280 байт. Я их впишу сюда, |
            ; пересоберу и снова запакую. Только тогда    |
            ; распаковщик отработает корректно.            |
            mov    [ldr_size], 2560;                      |
            ;----------------------------------------------'

            ; Установим указатель в файле
            ; на конец распаковщика (FILE_BEGIN+lde_size)
            invoke  SetFilePointer, [hModule], [ldr_size], 0, FILE_BEGIN
.loop:   
            ; Прочитаем наш заголовок (8 байт)
            invoke  ReadFile, [hModule], lnFileName, 8, nBytesRead, 0
            test    dword [nBytesRead], -1    ; если ничего не прочиталось,
            jz      .close_1            ; то выйдем (ошибка)

            movzx  eax, word [lnCmdLine]  ; eax = длина параметров

            ; Прочитаем командную строку из заголовка
            invoke  ReadFile, [hModule], szCmdLine, eax, nBytesRead, 0
            test    dword [nBytesRead], -1
            jz      .close_1

            movzx  eax, word [lnFileName]  ; eax = длина имени файла
            or      eax, eax    ; если 0 (файлов нет),
            jz      .close_1    ; то выйдем

            ; иначе прочитаем имя файла
            invoke  ReadFile, [hModule], szFileName, eax, nBytesRead, 0
            test    dword [nBytesRead], -1
.close_1:  jz      .the_end

            ; и создадим файл с именем szFileName
            invoke  CreateFile, szFileName, GENERIC_WRITE, FILE_SHARE_READ, 0, \
                                            CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, 0
            cmp      eax, -1    ; если не создался,
            je      .loop      ; то начнем заново
            mov    [hFiles], eax  ; сохраним хендл файла

            ; Выделяем память по размеру файла
            invoke  GlobalAlloc, GMEM_FIXED, [lnFileSize]
            or      eax, eax
            jz      .close
            mov    [hAllocMem], eax

            ; Прочитаем файл в выделенную память
            invoke  ReadFile, [hModule], [hAllocMem], [lnFileSize], nBytesRead, 0
            test    dword [nBytesRead], -1
            jz      .close

            ; А теперь из памяти запишем на диск
            invoke  WriteFile, [hFiles], [hAllocMem], [lnFileSize], nBytesRead, 0
.close:
            invoke  CloseHandle, [hFiles]      ; Закреом хендл файла (данных)
            invoke  GlobalFree, [hAllocMem]    ; Освободим память

            ; Запустим файл
            invoke  ShellExecute, 0, 0, szFileName, szCmdLine, 0, SW_SHOW
            jmp    .loop
.the_end::
            invoke  CloseHandle, [hModule]  ; закроем хендл лоадера
.exit:     
            invoke  ExitProcess, 0  ; выход

;-------------------------------------------------------------------------------
       
        section '.idata' import data readable

        library kernel32,'KERNEL32.DLL',\
                shell32,'SHELL32.DLL'
        include 'api\kernel32.inc'
        include 'api\shell32.inc'

;-------------------------------------------------------------------------------

Код:

          IV. ПОСЛЕСЛОВИЕ.

    В следующей части я покажу, как написать простейший билдер к нашему
лоадеру и получить полноценный склейщик файлов. А пока, в качестве
домашнего задания, попробуйте сделать билдер сами.
    Также, файлы легко склеить руками. Нам известен заголовок. Остается его
заполнить. Покажу на простом примере.
    Имеется файл fasmw.exe. Необходимо прикрепить его к лоадеру, чтобы тот
запустил его с параметрами "hello.asm".

    Итак: - имя файла: fasmw.exe (9 байт + 0 в конце, итого 10 или же 0Ah)
          - командная строка: hello.asm (тоже 9 байт + 0 в конце, итого 0Ah).
          - размер fasmw.exe: 122 880 байт (0001E000h)

То есть заголовок у нас будет такой:

      длина  длина      размер
        имени  комм.      файла
        файла  строки    fasmw.exe

        .-^-.  .--^--.  .----^----.
        0A 00  0A  00  00 E0 01 00  дальше идут параметры и имя файла.
        \  /  \    /    \        /
размер: WORD  WORD    DOUBLE WORD

    Всё! Теперь вы в начало fasm.exe запишем заголовок и смело весь файл
припишем в конец нашему лоадеру.

    Просто сравните два файла fasmw.exe до и после добавления заголовка
для нашего лоадера.

    файл fasmw.exe ДО обработки:

 00000000:  4D 5A 80 00-01 00 00 00-04 00 10 00-FF FF 00 00  MZА        **
 00000010:  40 01 00 00-00 00 00 00-40 00 00 00-00 00 00 00  @      @
 00000020:  00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00 
 00000030:  00 00 00 00-00 00 00 00-00 00 00 00-80 00 00 00              А
 00000040:  0E 1F BA 0E-00 B4 09 CD-21 B8 01 4C-CD 21 54 68    ║  ┤ ═!╕ L═!Th
 00000050:  69 73 20 70-72 6F 67 72-61 6D 20 63-61 6E 6E 6F  is program canno
 00000060:  74 20 62 65-20 72 75 6E-20 69 6E 20-44 4F 53 20  t be run in DOS
 00000070:  6D 6F 64 65-2E 0D 0A 24-00 00 00 00-00 00 00 00  mode.  $
 00000080:  50 45 00  -          -          -            PE             

 
  А теперь файл fasmw.exe ПОСЛЕ обработки:

          длина  длина    длина
            имени  комм.    файла        Далее идут командная
            файла  строки  fasmw.exe    строка и имя файла

 00000000:  0A 00  0A  00  00 E0 01 00  68 65 6C 6C-6F 2E 61 73      а  hello.as
 00000010:  6D 00  46  41  53 4D 57 2E  45 58 45 00-4D 5A 80 00  m FASMW.EXE MZЂ
                                                      ^^^^^^^^^^^
                                            отсюда пошёл fasmw.exe

 00000020:  01 00  00  00  04 00 10 00  FF FF 00 00-40 01 00 00          яя  @
 00000030:  00 00  00  00  40 00 00 00  00 00 00 00-00 00 00 00      @
 00000040:  00 00  00  00  00 00 00 00  00 00 00 00-00 00 00 00 
 00000050:  00 00  00  00  00 00 00 00  80 00 00 00-0E 1F BA 0E          Ђ    є
 00000060:  00 B4  09  CD  21 B8 01 4C  CD 21 54 68-69 73 20 70  ґ Н!ё LН!This p
 00000070:  72 6F  67  72  61 6D 20 63  61 6E 6E 6F-74 20 62 65  rogram cannot be
 00000080:  20 72  75  6E  20 69 6E 20  44 4F 53 20-6D 6F 64 65  run in DOS mode
 00000090:  2E 0D  0A  24  00 00 00 00  00 00 00 00-50 45 00    .  $        PE

    Вот мы и написали самый простой лоадер для самого простого джойнера.
Разобрали, как можно им пользоваться, даже без билдера.

    Я прошу новичков, что читают эту статью, попробуйте во всем разобраться,
чтоб было понятно всё, до последнего байта.

Не спрашивайте, ищите ответы _сами_.

Тогда и только тогда вы легко сможете сделать джойнер более навороченным,
добавить свой функционал.

becensed


Пишем простейший джойнер файлов. Часть 2.

Код:

Эта статья разбита на две части: 1. написание лоадера,
                                2. написание билдера.

   
Хочу сказать, что статья написана для _новичков_.
Люди знающие здесь ничего нового не узнают.



            I. ИНСТРУМЕНТЫ.

    В данной статье я покажу, как сделать простейший билдер для нашего
простейшего лоадера. Лично я буду компилировать всё в Microsoft Visual
Studio 2008, но каждый из вас может выбрать тот пакет, который ему больше
по душе.

    Итак, необходимое:
        - Microsoft Visual Studio 2008;
        - знание Си, WinApi;
        - терпение и чуток мозга :)


            II. ЧТО ТАКОЕ БИЛДЕР И КАК ОН РАБОТАЕТ?

    Билдер, в нашем случае, это программа, которая на входе получает файлы, а
на выходе - один .ехе-файл, при запуске которого запустятся все файлы, что
были даны билдеру. Да-да, это тот самый обычный GUI интерфейс, с кнопками
типа "Добавить файл", "Склеить файлы" и т.п. :)
  Работа билдера простая:
        1. Открываем лоадер (см 1-ую часть);
        2. Открываем файл из списка;
        3. Записываем первому файлу заголовок (8 байт)+параметры и имя;
        4. Пишем этот файл в конец лоадера.
        5. Если открыли все файлы, то выйдем, иначе пункт 2.

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

    Собственно, больше описывать нечего. Всё находится в исходниках билдера.
Не знаю, что там комментировать. Всё должно быть понятно.
Если нет - используйте MSDN, в 90% случаев найдете ответ именно там!

Немного прокомментирую содержимое архива:

joiner_1.txt - первая часть статьи
joiner_2.txt - вторая часть статьи (сейчас ее читаете)
|
+---exe
|      builder.exe    - файл билдера, уже скомпиленный.
|
\---src
    |  builder.cpp    - исходники билдера
    |  make.bat        - мейкфайл, пользуюсь консолью
    |
    \---res
            hkit.exe    - тулза, с помощью которой loader.exe превращается
                          в массив байт.

            loader.asm  - исходники лоадера
            main.ico    - иконка лоадера, суйте туда свою, если хотите
            Upack.exe  - пакер для лоадера.

           
            III. КОНЕЦ.

    Смотрите, изучайте. Проще придумать сложно. Сложнее придумать легко.
В этой статье, в двух ее частях, мы рассмотрели, как написать простой, но
полноценный джойнер любого количества файлов. Если у вас будут вопросы, я
постараюсь на них ответить. Только, пожалуйста, прежде, чем спросить
ПОИЩИТЕ ОТВЕТ САМОСТОЯТЕЛЬНО! :)

becensed

СКАЧАТЬ файлы к статье.

Для особо одаренных скажу сразу: это _простейший_ лоадер, его палит _любой_ антивирус. По данному вопросу критику игнорирую.


Время: 17:58