Описание динамически сгенерированного кода с помощью RtlAddFunctionTable в Windows x64

Мое приложение (написанное в Delphi, но это не имеет особого значения) генерирует куски кода динамически (он содержит встроенный компилятор). Чтобы исключения из Windows x64 работали корректно, мне нужно описать сгенерированные функции через RtlAddFunctionTable. До сих пор так хорошо, что я изменил генератор кода, чтобы использовать только официальные формы пролога и epilog, и я закодировал их, установив RUNTIME_FUNCTION, содержащий UNWIND_INFO и т.д.

Тем не менее, (преднамеренное) нарушение прав доступа в сгенерированном коде все еще приводит к немедленному завершению приложения, поэтому, очевидно, что-то не так. Используя windbg, я вижу следующее (где 0x4c5006f - адрес исключения):

0:000> .fnent 0x4c5006f 
Debugger function entry 00000000`05436910 for:

BeginAddress      = 00000000`00000000 
EndAddress        = 00000000`00000097
UnwindInfoAddress = 00000000`000000a0

Однако он не печатает информацию об отключении ниже этого. Я вижу это из окна памяти:

00000000`04c50000 53 56 57 55 41 54 41 55 41 56 41 57 48 83 ec 08  SVWUATAUAVAWH...
00000000`04c50010 49 89 ce 9b db e3 9b d9 3c 24 41 d9 ae e4 00 00  I.......<$A.....
00000000`04c50020 00 0f ae 5c 24 04 4c 89 f0 0f ae 90 e0 00 00 00  ...\$.L.........
00000000`04c50030 4d 8b be 28 01 00 00 4c 89 fd 49 8b 4f 08 48 85  M..(...L..I.O.H.
00000000`04c50040 c9 0f 84 1d 00 00 00 83 69 f0 01 0f 8f 13 00 00  ........i.......
00000000`04c50050 00 48 89 ca 4c 89 f1 48 83 ec 20 e8 70 41 ba fb  .H..L..H.. .pA..
00000000`04c50060 48 83 c4 20 48 33 c9 49 89 4f 08 49 8b 4f 08 48  H.. H3.I.O.I.O.H
00000000`04c50070 3b 09 ba 03 00 00 00 89 51 08 0f ae 54 24 04 9b  ;.......Q...T$..
00000000`04c50080 db e2 9b d9 2c 24 48 83 c4 08 41 5f 41 5e 41 5d  ....,$H...A_A^A]
00000000`04c50090 41 5c 5d 5f 5e 5b c3 90 90 90 90 90 90 90 90 90  A\]_^[..........
00000000`04c500a0 01 10 09 00 10 02 0c f0 0a e0 08 d0 06 c0 04 50  ...............P
00000000`04c500b0 03 70 02 60 01 30 00 00 00 00 00 00 00 00 00 00

При смещении 4c500a0 вы увидите информацию об отключении (01 10 09 00), затем девять записей, соответствующих следующему прологу:

  push  rbx
  push  rsi
  push  rdi
  push  rbp
  push  r12
  push  r13
  push  r14
  push  r15
  sub   rsp, $8

Я передаю 0x4c50000 в качестве базового адреса RtlAddFunctionTable. Почему windbg не распечатывает информацию для размотки? Я не понимаю, как работают смещения?

Сравните с обычной функцией Delphi:

0:000> .fnent 0x79caa9
Debugger function entry 00000000`05436910 for:

BeginAddress      = 00000000`0039ca70
EndAddress        = 00000000`0039caaf
UnwindInfoAddress = 00000000`005304d8

Unwind info at 00000000`009304d8, a bytes
  version 1, flags 0, prolog 8, codes 3
  frame reg 5, frame offs 0
  00: offs 8, unwind op 3, op info 0    UWOP_SET_FPREG
  01: offs 5, unwind op 2, op info 5    UWOP_ALLOC_SMALL
  02: offs 1, unwind op 0, op info 5    UWOP_PUSH_NONVOL

Кто-нибудь сумел заставить это работать в своем собственном коде и может указать мне в правильном направлении? Спасибо!

Ответ 1

Оказалось, что множество моих ошибок вызвало это. Я обнаружил и исправил их, и теперь раскручивание работает правильно. В частности, были случаи, когда RtlDeleteFunctionTable не вызывался до RtlAddFunctionTable, когда код был изменен. Также важно не касаться RSP в теле функции: это имеет смысл, но на самом деле не написано в документации MSDN.