Почему мы начинаем идти по сложному пути,говоря о эмуляции кода или гениальном девирте(мне действительно нужно взорвать дверь в деревне,когда можно просто сломать замок у входной двери?).
Опять же,можно просто по простому пути в данном случае, чтобы автоматизировать это чудо.
Это просто теория, которую можно реализовать т.к не вижу проблем в данном случае(поправьте,пожалуйста,если ошибаюсь и спасибо за знания :D).
Вспомним,например,статью
, благодаря чему можно пойти подумать и прийти к решению:
1)Выделяем память(old_fake_base) по определённому адресу(т.к в пофикшенном дампе должен быть STATUS_ACCESS_VIOLATION при вызове)для DLL,если у них есть DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size(допустим,мы не знаем точно весь список используемого импорта,хотя можно отфилтровать,чтобы они точно были системные DLL ).
Потребуется записать эти адреса на листочек(или куда вам нужно?),чтобы использовать в VEH обработчике.
2)Фиксим релоки в выделенной памяти и перенаправляем импорт скопированную DLL на скопированную DLL(KernelBase.dll ->ntdll.dll,например).
3)Проходим в PEB и делаем подмену DllBase на наши скопированные регионы(old_fake_base).
4)Ждём инит этого чуда,потом дампим,после его фикса с сервера(я не буду разбирать нужны ли эти доп. фиксы ).
5)Допустим,у нас всё заводится,кроме этого чуда-импорта(просто в теории т.к это не затрагивает пункт выше и мы не играем в догонялки)
Ставим наш VEH с условием,что у нас STATUS_ACCESS_VIOLATION и RIP является как раз теми адресами,где мы выделяли память для old_fake_base(начала и конец региона).
6)Можно просто менять на RIP= RIP - old_fake_base+ memory_dll(RIP используется в качестве RVA в данном случае). Лучше сделать проход по PIMAGE_EXPORT_DIRECTORY для RIP - old_fake_base(можно вшить массивом old_fake_base или оставить на диске) ,чтобы получить оригинальное имя и если мы получили имя, мы уже получить RVA в memory_dll(не old_fake_base,которая была в памяти при дампе) т.к с большим шансом у других юдей будет различаться DLL -> RVA разные.
Да,каждый вызов импорта - вызов VEH,но лучше кэшировать результат,чтобы не делать заново расчёты и потом можно использовать при долгом ручном фиксе.
P.S извиняюсь, что слишком длинно расписано, но тут больше слов, нежели действий.