Используется при защите соединений с базами данных (сфера информационных технологий). В двух словах: при перехвате функции создания соединения с MSSQL-сервером выполняется функциональность дополнительной двухфакторной аутентификации для последующего зашифрования/расшифрования передаваемых данных «на-лету».
Замечу, правда, что приведенный по ссылке метод не является переносимым (т.е. может работать не всегда). И вот почему:
1) Иногда поток управления проходит через переходник, минуя адрес в таблице слотов;
2) Способ определения адреса слота в таблице MethodTable может изменяться с каждой версией CLR;
3) Вызов
в командах jmp NativeCode (см. описание FixupPrecode после компиляции) находят смещение сгенерированного кода относительно окончания самих команд (которые занимают 5 байт);
5) Следующая строка вычисляет относительное смещение сгенерированного внедряемого кода относительно окончания команды jmp NativeCode для перехватываемого кода(!!!) и записывает его в команду jmp NativeCode для перехватываемого кода
Команда mov eax, imm для x86 содержит 4-байтовый операнд imm по смещению 1 от начала команды (которая занимает 5 байт).
Команда mov rax, imm для x64 содержит 8-байтовый операнд imm по смещению 2 от начала команды (которая занимает 10 байт).
Способ вызова через переходники не меняется с самого начала CLR (указанный способ можно мониторить в исходниках CLR на github).
Единственное, что приходится учитывать — не изменилась ли реализация ThePreStub, поскольку способ поиска PrestubWorker основывается на том, что ThePreStub не вызывает других функций, кроме PrestubWorker. Функции ThePreStub нет в исходниках (поскольку она реализована на ассемблере), приходится проверять на практике.
Да, для простоты в примере приведен класс для одного метода.
Но в этом же классе можно перехватить сколько угодно методов для произвольных классов.
Для этого в функции GetTypes нужно указать все требуемые классы, а в функции OnLoad
(в зависимости от принятого класса) перехватить сколько угодно его функций.
Пока, особо не афишируясь, используется в промышленных разработках, где необходимо перехватывать функции .NET.
Например, при перехвате обращений к базе данных SqlServer для обеспечения безопасного соединения.
https://github.com/dotnet/coreclr/blob/775003a4c72f0acc37eab84628fcef541533ba4e/Documentation/botr/method-descriptor.md
1) Иногда поток управления проходит через переходник, минуя адрес в таблице слотов;
2) Способ определения адреса слота в таблице MethodTable может изменяться с каждой версией CLR;
3) Вызов
RuntimeHelpers.PrepareMethod(methodToReplace.MethodHandle);
не всегда гарантирует выполнение JIT-компиляции;
4) Переходники могут не быть FixupPrecode (особенно для NGen-модулей).
int* inj = (int*)methodToInject.MethodHandle.Value.ToPointer() + 2;
long* inj = (long*)methodToInject.MethodHandle.Value.ToPointer()+1;
только в размерности адреса.
2) Следующие строки (скорее всего) получают адреса слотов в таблице MethodTable (см. приведенную картинку)
int* inj = (int*)methodToInject.MethodHandle.Value.ToPointer() + 2;
int* tar = (int*)methodToReplace.MethodHandle.Value.ToPointer() + 2;
3) Следующие строки определяют адрес переходника FixupPrecode после компиляции для двух функций
byte* injInst = (byte*)*inj;
byte* tarInst = (byte*)*tar;
4) Следующие строки
int* injSrc = (int*)(injInst + 1);
int* tarSrc = (int*)(tarInst + 1);
в командах jmp NativeCode (см. описание FixupPrecode после компиляции) находят смещение сгенерированного кода относительно окончания самих команд (которые занимают 5 байт);
5) Следующая строка вычисляет относительное смещение сгенерированного внедряемого кода относительно окончания команды jmp NativeCode для перехватываемого кода(!!!) и записывает его в команду jmp NativeCode для перехватываемого кода
*tarSrc = (((int)injInst + 5) + *injSrc) — ((int)tarInst + 5);
Таким образом, в переходнике команда jmp NativeCode(Source) заменяется на jmp NativeCode(Inject)
6) В Release-версии адрес напрямую заменяется в слотах таблицы MethodTable;
Команда mov rax, imm для x64 содержит 8-байтовый операнд imm по смещению 2 от начала команды (которая занимает 10 байт).
Единственное, что приходится учитывать — не изменилась ли реализация ThePreStub, поскольку способ поиска PrestubWorker основывается на том, что ThePreStub не вызывает других функций, кроме PrestubWorker. Функции ThePreStub нет в исходниках (поскольку она реализована на ассемблере), приходится проверять на практике.
Команда смысла не несет, а используется в качестве идентифицирующего признака переходника.
Но в этом же классе можно перехватить сколько угодно методов для произвольных классов.
Для этого в функции GetTypes нужно указать все требуемые классы, а в функции OnLoad
(в зависимости от принятого класса) перехватить сколько угодно его функций.
Например, при перехвате обращений к базе данных SqlServer для обеспечения безопасного соединения.