Задать вопрос
@igreklpofrss

Как из одного сегмента вызвать функцию другого сегмента?

Опишу задачу и сразу скажу, что познания по ассемблеру поверхностные, изучал для понимания того, что происходит в дебаге IDA, но в целом понятны, логика ясна и хотелось бы решить проблему именно ассемблером. Поэтому инъекцию dll не предлагать!

Имеется exe файл игры. В файле есть функции для импорта, экспорта текстур. Хотелось бы научить игру запускать их. Для анализа exe была использована IDA PRO 9 и изменить ассемблер код также планировал в IDA. (Но если есть аналоги, ибо IDA не удобная для написания ассемблера, то подскажите варианты). Изучив работу exe файла было принято такое решение:

У игры есть собственное подобие препроцессора С/С++ и его синтаксиса, написаного разработчиками.(Нет, это не оригинальные препроцессорные команды!) Т.е. есть файл name.ini в папке игры и в нем задаются константы ботов, уровней сложности и т.д. с синтаксисом препроцесса #define, #include, #if и т.п. Пример:
// file.ini
// Cars
#define OFFROAD 0
#define HIGHWAY 1
// и т.д.
// Game difficult
#define EASY 1
#define NORMAL 2
// и т.д.

Чтением этих файлов занимается "функция-парсер-компилятор" gameHandlePreproc. Она проверяет ключевое слово(#define, #include и т.д.), сверяет с шаблонам допустимых значений и решает какую функцию запустить. Я решил добавить в этот шаблон свое ключевое слово(может #skinconverter?), чтобы запускать функции импорта-экспорта. Если правильно понял, то внедрить в середину свою функцию нет возможности. Все адреса сегмента уже заняты и двигать стек нет смысла все сломается. Я так понял, что решением задачи будет создать новый сегмент и там писать свои команды. Таблица сегментов от IDA после дезассемблирования такова + мой сегмент:
┃ Segment name ┃   Start    ┃     End     ┃  Base  ┃
┃   .text      ┃ 0x00401000 ┃  0x00554000 ┃  0001  ┃
┃   .idata     ┃ 0x00554000 ┃  0x0055432C ┃  0002  ┃
┃   .rdata     ┃ 0x0055432C ┃  0x0055E000 ┃  0002  ┃
┃   .data      ┃ 0x0055E000 ┃  0x01661000 ┃  0003  ┃
┃   .data1     ┃ 0x01661000 ┃  0x01662000 ┃  0004  ┃
Мой сегмент:
┃   .text      ┃ 0x7FFF0000 ┃  0x80040000 ┃  0001  ┃

Почему мой сегмент так далеко. Если запустить игру, то, с учетом dll библиотек, будут заняты адреса от 0x00010000 до +- 0x7FFE0500. Если создать ближе к сегменту data1, то сегменты библиотек и дебага переписывают данные поверх.

Я решил протестировать как это будет работать и как реализовать задумку. В "функции-парсере-компиляторе" есть функция обработки ошибки, которая просто выводит ошибку в log файл. Решил этот участок кода выделить под вызов своей функции и в своей функции описсать всю дальнейшую логику. Но предварительно решил протестировать как это будет работать. Как из одного сегмента вызвать функцию другого сегмента. Для теста перенес вызов вывода ошибки в свою функцию, но после запуска измененного кода получаю ошибку:
// 1. Кусок функции gameHandlePreproc, который обрабатывает ошибку.
// Без изменений!
// Родной .text сегмент
.text:0042164B loc_42164B:    ; CODE XREF: gameHandlePreproc+23E↑j
.text:0042164B     push    edi   ; ArgList // в edi хранится слово из file.ini для сверки с шаблонами
.text:0042164C     push    offset aUndefinedWord ; "Undefined word on line..." 
.text:00421651     call    tioError // вывод ошибки в лог файл
.text:00421656     add     esp, 8

// 2. Что хочу сделать так:
// Родной .text сегмент
.text:0042164B loc_42164B:     ; CODE XREF: gameHandlePreproc+23E↑j
.text:0042164B     push    edi    ; ArgList // в edi хранится слово из file.ini для сверки с шаблонами
.text:0042164C     push    offset aUndefinedWord ; "Undefined word on line..."
.text:00421651     call    mySuperFunction
.text:00421656     add     esp, 8
...
...
...
// Мой .text сегмент
.text:7FFF0000 ; Segment type: Pure code
.text:7FFF0000 ; Segment permissions: Read/Execute
.text:7FFF0000 _text           segment para public 'CODE' use32
.text:7FFF0000       assume cs:_text
.text:7FFF0000       ;org 7FFF0000h
.text:7FFF0000       assume es:nothing, ss:nothing, ds:_data, fs:nothing, gs:nothing
.text:7FFF0000
.text:7FFF0000 ; =============== S U B R O U T I N E ==============================
.text:7FFF0000
.text:7FFF0000 mySuperFunction    proc near   ; CODE XREF: gameHandlePreproc+271↑p
.text:7FFF0000       call    tioError   // вывод ошибки в лог файл
.text:7FFF0000 ; ---------------------------------------------------------------------------
.text:7FFF0005       db    0
.text:7FFF0006       db    0
.text:7FFF0007       db    0
.text:7FFF0007 mySuperFunction    endp

// Получаю ошибку:
7FFF0000: The instruction at 0x7FFF0000 referenced memory at 0x7FFF0000. The memory could not be executed -> 7FFF0000 (exc.code c0000005, tid 6332)

При том, после запуска программы, функция меняется на:
.text:7FFF0000 mySuperFunction    db  0
.text:7FFF0001                    db  0

Как правильно реализовать задачу? В чем проблема? В IDA или в ассемблере? Как починить?
  • Вопрос задан
  • 105 просмотров
Подписаться 1 Сложный Комментировать
Пригласить эксперта
Ответы на вопрос 3
jcmvbkbc
@jcmvbkbc
"I'm here to consult you" © Dogbert
// Получаю ошибку:
7FFF0000: The instruction at 0x7FFF0000 referenced memory at 0x7FFF0000. The memory could not be executed -> 7FFF0000 (exc.code c0000005, tid 6332)

В чем проблема?

По видимому в том, что вызвать функцию получилось, но память по адресу 7FFF0000 -- не исполняемая.

Как починить?

Начать с того, что сделать память по адресу 7FFF0000 исполняемой. После этого вывод в лог должен заработать, но будет падать после него, потому что функция mySuperFunction не возвращается.
Ответ написан
@mvv-rus
Настоящий админ AD и ненастоящий программист
Мой сегмент:
┃ .text ┃ 0x7FFF0000 ┃ 0x80040000 ┃ 0001 ┃

Так делать не надо: 0x80000000 - это граница адресного пространства пользователя для обычных 32-битных процессов, не помеченных как использующие расширенное адресное пространство. А перед ней располагаются используемые ядром данные (информация о процессе и потоках в нем), размещаемые в памяти пользовательского режима. То есть, вы напрасно думаете, что вы разместили сегмент в свободной, никем не используемой памяти.
Ответ написан
Vapaamies
@Vapaamies
Психанул и снес свои ответы не отмечающим решения…
Если запустить игру, то, с учетом dll библиотек, будут заняты адреса от 0x00010000 до +- 0x7FFE0500.

Странно видеть подобное утверждение в 2024-м. Что, ASLR в системе глобально отключено?

У библиотек непременно есть перемещаемые символы («релоки»), и если вы модифицируете exe-шник, они спокойно перелезут на другие адреса при запуске. Так что, это вообще не повод париться.

Зато мне, вот, странно, что сформированный подобным образом «с дыркой» PE-файл в принципе запустился. Адресное пространство разве не должно быть непрерывным? Всегда казалось, что именно так.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы