Декомпиляция и вызов функции по адресу

ОГЛАВЛЕНИЕ

Назначение статьи – показать, как вызвать функции другой программы через вашу собственную. Это руководство будет разбито на серию шагов с общим примером, сопровождаемым применением знаний к настоящей программе.

•    Скачать демо - 75.92 Кб
•    Скачать исходный код - 8.36 Кб

Необходимые инструменты

Эта статья использует отладчик (OllyDbg можно бесплатно скачать тут) и инжектор DLL Winject (бесплатно скачивается через интернет и включен в callbin.zip). Нужен компилятор C++ для компиляции кода для DLL. Компиляция тестового приложения и попытка работы над ним, скорее всего, дадут разные результаты из-за настроек компилятора, поэтому лучше всего использовать приложение, включенное в callbin.zip.
Зачем делать это?

Есть много причин для декомпиляции вызовов к внутренним функциям процессов. Например, дополнения или модификации для игр используют прием вызова внутренних функций процесса (игры), чтобы отобразить текст на экране. Возможно, надо расширить функционал горячих клавиш программы или же надо вызвать функцию “win” в игре. Так или иначе, данный прием применяется гораздо шире, чем указано здесь. Это руководство требует хорошего значения ассемблера x86 и некоторого знания Win32 API.

Написание тестового приложения

Сначала пишется приложение, затем оно декомпилируется, чтобы увидеть, как вызывается эта функция. Будет создано простое приложение, выполняющее арифметические действия и выводящее значения с помощью функции. Ниже приведено тестовое приложение:

#undef UNICODE

#include <span class="code-keyword"><windows.h></span>
#include <span class="code-keyword"><stdio.h></span>

void mySecretFunction(int* param1, const char* param2,
                      DWORD param3, BYTE param4)
{
    printf("----------Function Entry----------\n");
    *param1 += 2008;
    printf("param1: %i\n", *param1);
    printf("param2: %s\n", param2);
    param3 *= *param1;
    printf("param3 (param3 *= *param1): %i\n", param3);
    param4 = 0x90;
    printf("param4: %i\n", param4);
    printf("----------Function Exit----------\n");
}
int main(void)
{
    int anArgument = 123;
   
    for(;;)
    {
    if(GetAsyncKeyState(VK_F11) & 1)
        mySecretFunction(&anArgument,
                         "This is the original text!",
                         123456, 4);
    else if(GetAsyncKeyState(VK_F1) & 1)
        break;
    }

    return 0;
}

Эта функция принимает четыре параметра (1 – по ссылке, 3 – по значению). Большое преимущество написания собственного приложения и его декомпиляции заключается в том, что вы уже знаете, что искать. Пытаясь вызвать функцию по адресу приложения, исходного кода которого у вас нет, вы должны быть готовы проделать большую лишнюю работу, так как это трудная задача. Будет декомпилирована отладочная сборка программы (включенная в callbin.zip), чтобы увидеть, что именно происходит по шагам. Почему отладочная сборка, а не выпускаемая сборка с выключенными оптимизациями? Это слегка облегчает анализ для отладчика, но не играет роли. Если посмотреть на выпускаемую сборку программы, в зависимости от оптимизаций компилятора, mySecretFunction(...) может быть встроена в main, и программа может выглядеть иначе, так как параметры могут храниться в статических местах. Плюс, так как функция может быть встроенной, попытка вызвать ее по адресу может быть тщетным поиском. Анализ начинается с открытия OllyDbg.