Задать вопрос
@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 или в ассемблере? Как починить?
  • Вопрос задан
  • 145 просмотров
Подписаться 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-файл в принципе запустился. Адресное пространство разве не должно быть непрерывным? Всегда казалось, что именно так.
Ответ написан
Комментировать
Ваш ответ на вопрос

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

Похожие вопросы
AST Москва
от 400 000 ₽
Data World Москва
от 180 000 до 210 000 ₽
Wanted Москва
от 150 000 до 200 000 ₽