Отлично, вы уже запустили какой-то код. Но соглашение вызова так и не выдержали.
1. Используйте соглашение вызова STDCALL. Тогда функция должна будет сама подчищать за собой. Поскольку в ней нет никаких локальных переменных, и подчистка не потребуется.
typedef uint32_t WINAPI (*SomeFunc)();
uint32_t result = (SomeFunc)exec();
Заодно это позволит увидеть, что функция запустилась. В любом случае возвращаемое значение будет в eax.
2. В конце нашей функции поставьте RET (опкод CB, если я не ошибаюсь).
3. Вычислите и впатчите в наш код адрес переменной.
я думал тут адрес указывается относительно текущего сегмента, как тогда быть?
Начиная с защищённого режима 386, у нас «плоская память». Сегменты используются только загрузчиками, ВМами и прочей шушерой. Просто вычислите 32-битный адрес и запишите куда надо.