Лучший способ объявить функции-члены в файле hpp, которые указывают на существующий адрес памяти - PullRequest
0 голосов
/ 25 августа 2018

Из-за изменений в компиляторе MVSC x86 / x64 я не могу использовать __asm(jmp addr) для выполнения прямых функций через адрес памяти в проекте.

Для функций, не являющихся членами, эти вопросы и ответы сделали свое дело: заменили эпилог функции хвостового вызова встроенной сборки на Intrinsics для x86 / x64 msvc

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

zstring.hpp:

#pragma once

#include <string>
#include "../asmjmp.h"

int __cdecl operator==(class zSTRING const &, char const * const);
int __cdecl operator==(class zSTRING const &, class zSTRING const &);

class zSTRING
{
public:
    zSTRING() {
        __asm( jmp 0x00402AF0);
    }

    zSTRING(zSTRING const &) {
        __asm( jmp 0x00416500);
    }

    ~zSTRING() {
         __asm( jmp 0x00401160);
    }

   int Overwrite(unsigned int, class zSTRING const &) {
        __asm( jmp 0x0046B6A0);
    }

    int Overwrite(string) {
        __asm( jmp 0x0046B6FF);
    }

    int Insert(unsigned int, class zSTRING const &) {
        __asm( jmp 0x0046B400);
    }

    /* My Variant: */
    int (*Insert)(unsigned int, class zSTRING const &) = ((int(*)(unsigned int, class zSTRING const &))0x0046B400);

    int Delete(class zSTRING const &, enum zTSTR_KIND) {
        __asm( jmp 0x0046BCF0);
    }

    /* My Variant: */
    int (*Delete)(class zSTRING const &, enum zTSTR_KIND) = ((int(*)(class zSTRING const &, enum zTSTR_KIND))0x0046BCF0);

    char * ToChar() const
    {
        __asm( jmp 0x004639D0);
    }

    zSTRING Copied(unsigned int, unsigned long) const
    {
        __asm( jmp 0x0046C170);
    }

    zSTRING &operator+=(char const *) {
        __asm( jmp 0x0067A7B0);
    }

    /* My Variant (without knowing if it would work): */
    zSTRING (*&operator+=)(char const *) = ((zSTRING (*)(char const *))0x0067A7B0);

    char & operator[](unsigned int) {
        __asm( jmp 0x00445A20);
    }

    /* My Variant (without knowing if it would work): */
    char & (operator[])(unsigned int) = ((char &(*)(unsigned int))0x00445A20);
};

1 Ответ

0 голосов
/ 27 августа 2018

если вы хотите, чтобы какая-то функция содержала одну jmp инструкцию по какому-либо адресу - вам нужно объявить ее с __declspec(dllimport) (это Microsoft Specific и работает только для CL компилятора, но вы считаете, что другие компиляторы имеют эквивалентсинтаксис).скажем, например,

void __declspec(dllimport) TrimLeft(char);

, если вам нужно это для всех функций-членов в классе - объявите все классы следующим образом:

class __declspec(dllimport) zSTRING
{
    zSTRING();
    // .. more declarations
};

это повлияет на все не виртуальные функции-члены истатический член данных для класса, подобный объявленному с __declspec(dllimport)

, когда функция объявлена ​​с __declspec(dllimport), компилятор объявил переменную указателя extern:

extern void* __imp___FUNCDNAME__;

где __FUNCDNAME__оформлено название функции + __imp_ префикс;и каждый раз, когда вы вызываете такую ​​функцию, компилятор генерирует инструкцию call __imp___FUNCDNAME__ (после передачи аргументов функции в регистры или стек).с Редактировать и продолжить компилятор опций обычно генерирует менее оптимизированный код:

call func
func:
jmp __imp___FUNCDNAME__ ; exactly what you try - single jmp in function body

, который фактически эквивалентен одному call __imp___FUNCDNAME__

, теперь очевидно, что для каждой импортируемой функцииvoid* __imp___FUNCDNAME__ должен быть где-то определен и содержать реальный адрес функции.в противном случае вы получили известную ошибку компоновщика LNK2001: неразрешенный внешний символ

обычно мы используем LIB файл, где точно определены __imp___FUNCDNAME__ символы - в этом случае компоновщик поместит всю эту переменную __imp_* в IAT раздел PE и опишите его в разделе импорта.в результате загрузчик назначает правильный адрес для каждого __imp___FUNCDNAME__ при загрузке изображения.

, если вы пытаетесь импортировать эту функцию из некоторой библиотеки DLL и из этой библиотеки DLL экспортировать этой функции - вы должны иметь LIB файл для этого.даже если у вас нет LIB - вы можете легко создать себя сами - создайте отдельный проект с именем выходного файла, точно совпадающим с именем dll, из которого вы будете вызывать код, и "реализовать" все эти функции с помощью __declspec(dllexport) длякаждая функция или класс.Реализация каждой функции - может быть пустой или одиночной return 0; - на самом деле, когда мы собираем lib - она ​​не содержит никакого кода (в результате реализация может быть фиктивной / пустой).он содержит точно имена функций и имя dll (потому что это имя выходного файла для проекта должно быть точно именем dll. но сказать, что цель проекта - exe или dll - не имеет значения).в общем - это должно выглядеть так:

void __declspec(dllexport) TrimLeft(char)
{
}

class __declspec(dllexport) zSTRING
{
public:
    zSTRING()
    {

    }

    int Overwrite(unsigned int, class zSTRING const &)
    {
        return 0;
    }
    //...
};

Вы легко создаете этот код и получаете LIB файл (библиотека импорта) все, что вам нужно.

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

, потому что __FUNCDNAME__ обычно содержит недопустимые символы C/C++ - вам нужно будет объявить его в asm, например:

_BSS segment

__imp_?TrimLeft@@YAXD@Z DQ ?
__imp_??0zSTRING@@QEAA@XZ DQ ?
__imp_??1zSTRING@@QEAA@XZ DQ ?
__imp_??0zSTRING@@QEAA@AEBV0@@Z DQ ?
__imp_?Insert@zSTRING@@QEAAHIAEBV1@@Z DQ ?

public __imp_?TrimLeft@@YAXD@Z
public __imp_??0zSTRING@@QEAA@XZ
public __imp_??1zSTRING@@QEAA@XZ
public __imp_??0zSTRING@@QEAA@AEBV0@@Z
public __imp_?Insert@zSTRING@@QEAAHIAEBV1@@Z

_BSS ends

и реализовать функцию для разрешения импорта самостоятельно

resolveimport proc

    lea rax,[rcx + rva_1]
    mov __imp_?TrimLeft@@YAXD@Z,rax

    lea rax,[rcx + rva_2]
    mov __imp_??0zSTRING@@QEAA@XZ,rax

    ;...

    ret

resolveimport endp

скажем, вы звоните resolveimport из c ++ кода с адресом dll - resolveimport(LoadLibraryW(L"my.dll"));

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...