2009-11-18 3 views
1

Это будет звучать супер хаки, но кто-нибудь знает способ комбинирования тел метода во время выполнения на C++? Я сейчас нахожусь на пути захвата адреса функций, а затем memcopy в исполняемую память, но у него есть проблема с нежелательным прологом/эпилогом.Объединение тел функций во время выполнения

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

+1

@ Hippiehunter: да, это действительно здорово хакерский – RageZ

+2

@RageZ: Да, но это также кажется довольно аккуратным. Запретный плод, я думаю. – bcat

+0

Вы хотите, чтобы вы цепочки операций функций? – Alon

ответ

3

Я не вижу практического применения для этого, кроме черт его :-)

Итак, сначала вы должны принять решение о платформе. Нет никакого способа в аду, вы можете сделать это кросс-платформенным способом. На самом деле это может быть довольно сложно сделать так, чтобы работать с несколькими компиляторами, даже на одной платформе. Тогда тип процессора, конечно :-)

Тогда вам следует как-то проверить, что код, который вы копируете, можно переместить. Не все может быть перемещено, как вам хочется.

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

Знайте, как пролог/эпилог, сгенерированный вашим компилятором. Возможно, вы можете обманывать, добавляя в начале и конце функций некоторые кодовые последовательности, которые ничего не делают, но вы можете использовать их как подпись, а затем искать (например, nop; nop; nop; xor ax, ax; nop; push ax; pop ax; nop; nop; nop;). Удостоверьтесь, что компилятор не оптимизирован :-)

Убедитесь, что вы можете написать/выполнить этот код. Современные процессоры и ОС обычно не позволяют писать в сегменте кода или выполнять сегмент без кода. Таким образом, вам нужно будет выяснить, каковы способы изменения прав (100% ОС).

Затем получайте удовольствие от боевых действий, таких как «рандомизация расположения пространства адреса», «рандомизация стека», «предотвращение выполнения данных», «рандомизация кучи».

В любом случае, много работы. И бессмысленно, за исключением того, что у вас есть хороший вызов и в процессе выучите некоторые сборки и внутренние элементы ОС. O для доказательства себя как «1337», но потом, спрашивая о том, как это сделать, не совсем «1337», если вы спросите меня :-)

В любом случае, удачи.

+0

Выполнение кучи Не проблема (несколько вызовов и что-то исполняемо), и я действительно получил это, работая в простых случаях уже. Наверное, меня сжигали неперемещаемые инструкции. Но теперь, когда упоминалось, редкие инструкции или setjmp звучат как отличный способ скопировать копии памяти. Я отвечу на ваш ответ, потому что его самый полный, но полный ответ требует многих методов. – Hippiehunter

+0

Hi Mihai - вы не возражаете, если я спрошу, полностью ли вы отбросили всю поддержку своего очень здорового продукта ansi econsole? Я не смог установить его в последнем затмении Luna. Я смог вручную добавить некоторые вещи в область плагина и показать значки, но консоль не отображала коды esc. Сказал, что у него была зависимость Java 6 SE, но я думаю, что Луна - 7. Не уверен, что это фактор. Ваш сайт отключен? Я видел комментарий от кого-то другого, что они получали ту же самую ошибку, пытающуюся установить с рынка (после этого я посетил пакет и ваш github proj). – clearlight

+0

Нет, я все еще поддерживаю это. Похоже, у моего интернет-провайдера были некоторые проблемы, но я думал, что это временно. Я подготовил альтернативный сайт на GitHub, и если ошибки продолжатся, я перевещу его туда. Но это не должно иметь ничего с Java 6 ... Можете ли вы сообщить об ошибке (с сообщением об ошибке) на странице https://github.com/mihnita/ansi-econsole/issues? Или оставить комментарий здесь, если у вас нет учетной записи github: https://mihai-nita.net/java/ Большое спасибо. –

3

Почему бы не использовать сборку?

+0

, так как это во время выполнения, (исправьте меня, если я ошибаюсь) мне нужно будет выполнить работу ассемблера во время выполнения, я думаю, меня просто не интересует такая работа. – Hippiehunter

0

Это немного тяжеловесный, поэтому он может и не быть тем, что вы ищете, но вы всегда можете попробовать использовать LLVM в JIT свой код.

4

Простым решением для достижения того, что вы хотите, является сохранение указателей функций в массиве.
Затем вы можете создавать во время выполнения указатели функций (ваши скрипты).
Вы выполняете скрипт, итерируя список и вызывая функции.

+0

Этот подход не позволяет избежать вызовов. – Gonzalo

2

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

Если бы я должен был сделать что-то вроде вас, я бы поставил два setjmp в начале и в конце подпрограмм, выделив исполняемую память (возможно ли это? Я думал, что текстовый раздел был только для чтения, но да ... переполнение буфера использует работу, выполняя стек, так что да, я думаю, вы можете, если у вас нет технологии NX), затем скопируйте кусок из четырех найденных скобок setjmp. В этом процессе у вас будет беспорядок с кодами операций, которые не могут перемещаться, я думаю, что, если они ссылаются на абсолютный адрес других кодов операций в вашем ассемблерном коде. Конечно, если вы копируете, этот адрес должен быть соответствующим образом изменен.

Удачи.

+0

@Stefano Borini, справа. @Hippiehunter, вы можете сделать это в системе, сделанной Microsoft или самим собой. – Test

2

шаблоны?

Если у вас есть это:

template <int Operations> 
struct Foo 
{ 

    static void Do(int a, int b) 
    { 
    if (Operations & 1) 
    { 
     /// op 1 
    } 

    if (Operations & 2) 
    { 
     /// op 2 
    } 
    if (Operations & 4) 
    { 
     /// op 3 
    } 
    //... 
    } 
}; 

оптимизатор будет отбрасывать блоки, которые не относятся к вашей специализации, например,

Foo<6>::Do(77,88)

будет обрабатывать только 2-й и 3-й шаг, а не генерировать код для 1-го ор.

(Вы должны проверить выход вашего компилятора, но большинство должно быть в состоянии. Я использую это для проверки выбранных свойств массива, работал под VC6 и более поздними компиляторов, как шарм)

1

Для Trully динамических решение ... физически объединяющее их:

  • Предположим, что ваши операции занимают одинаковое количество инструкций.
  • Удостоверьтесь, что они оставляют параметры неповрежденными (т. Е. Оставить стек и регистры вызовов нетронутыми).
  • Отметьте конец с помощью редкой инструкции.

Теперь вы готовы построить вектор инструкции:

struct Op { 
    char code[MAX_OP_SIZE]; 
    public Op(const void (*op)()) { 
     /* fill code with no-ops */ 
     const char* opCode = (char*)op; 
     char* opCodeCopy = &code[0]; 
     while (*opCode != GUARD_INSTRUCTION) { 
      *opCodeCopy++ = *opCode++; 
     } 
    } 
} 

std::vector<Op> combinedOperation; 

combinedOperation.push_back(op1); 
// ... 

void (*combinedOpFunc)(params) = void *()(params)&combinedOperation[0]; // not sure about syntax here 

combinedOpFunc(data); 
// start praying 

Другой вариант, чтобы не копировать код на всех. Это вариант ответа Стано Борини с помощью setjmp/longjmp. Вместо того, чтобы прыгать в каждом методе, мы устанавливаем их один раз.

Если оп выглядел:

void op1(paramtype param) { ... } 

делают ЬурейеЕ недействительным (* OPTYPE) (Paramtype);

void op1(paramtype param, OPTYPE nextop, jmp_buf returnFrame) 
{ 
    // ... 
    if (nextop) { 
     // place (paramtype+1) into argument slot 2 (assume +1 does pointer arithmetic) 
     // update program counter to value of paramtype 
    } else { 
     longjmp(returnFrame, 0); 
    } 
} 

Затем составьте список операций:

std::vector<OPTYPE> ops; ops.push_back(&op1); // ... 

и вспомогательную функцию

void callOps(const std::vector<OPTYPE>& opList, paramtype param) 
{ 
    OPTYPE firstOp = (OPTYPE)&opList[0]; 
    jmp_buf frame; 
    int ret = setjmp(&frame); 
    if (ret == 0) firstOp(param, firstOp + 1, frame); 
} 

и выполнить его

callOps(ops, opParameter);