Главная » Хабрахабр » Пропихиваем параметры в небезопасные операции в безопасном коде

Пропихиваем параметры в небезопасные операции в безопасном коде

Всем привет. В этот раз продолжаем смеяться над нормальным вызовом методов. Предлагаю ознакомится с вызовом метода с параметрами без передачи параметров. Также попробуем преобразовать ссылочный тип в число — его адрес, без использования указателей и unsafe кода.

Дисклеймер

Прежде, чем приступить к повествованию, настоятельно рекомендую ознакомиться с предыдущим постом про StructLayout, т.к. вещи, оговоренные там, здесь повторяться не будут.

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

Немного начальных сведений

Прежде, чем приступить к практике, давайте вспомним, как преобразовывается код C#.
Разберем простой пример. Напомню, что для того, чтобы развлекаться со StructLayout, я использую только виртуальные методы.

public class Helper
} public class Program { public void Main() { Helper helper = new Helper(); var param = 5; helper.Foo(param); }
}

Данный код не содержит ничего сложного, однако инструкции, генерируемые JiTом, содержат несколько ключевых моментов. Предлагаю разобрать лишь небольшой фрагмент сгенерированного кода.

1: mov dword [ebp-0x8], 0x5 2: mov ecx, [ebp-0xc] 3: mov edx, [ebp-0x8] 4: mov eax, [ecx] 5: mov eax, [eax+0x28] 6: call dword [eax+0x10]

В этом небольшом примере можно наблюдать fastcall — соглашение по передаче параметров через регистры (первых двух параметров слева направо в регистрах ecx и edx), а остальные параметры передаются справа налево в стеке. Первый (неявный) параметр — адрес экземпляра класса, на котором метод вызван. Он передается первым неявным параметром для каждого экземплярного метода. Второй параметр — локальная переменная типа int (в нашем случае).

Это адрес непосредственно таблицы методов.
Третяя строка содержит копирование локальной переменной 5 в регистр edx
Четвертая строка копирует адрес таблицы методов в регистр eax
Пятая строка содержит сдвиг регистра eax на 40 байт, на адрес начала методов в таблице методов. Итак, в первой строке мы видим локальную переменную 5, здесь ничего интересного нет.
Во второй строке мы копируем адрес экземпляра Helper в регистр ecx. к такой информации, например, относится адрес таблицы методов базового класса, адрес EEClass, различные флаги, в том числе и флаг сборщика мусора, и так далее). (Таблица методов содержит разную информацию, которая хранится до этого. Почему наш единсвенный метод пятый? Соответсвенно теперь в регистре eax хранится адрес первого метода из таблицы методов.
В шестой строке вызывается метод по смещению 16 от начала, то бишь пятый в таблице методов. Напоминаю, что у object существуют 4 виртуальных метода(ToString, Equals, GetHashCode и Finalize), которые, соотвественно, будут у всех классов.

Переходим к практике

Пришло время приступить к небольшой демонстрации. Предлагаю вот такую заготовку (весьма похожую на заготовку из предыдущей статьи).

[StructLayout(LayoutKind.Explicit)] public class CustomStructWithLayout { [FieldOffset(0)] public Test1 Test1; [FieldOffset(0)] public Test2 Test2; } public class Test1 { public virtual int Useless(int param) { Console.WriteLine(param); return param; } } public class Test2 { public virtual int Useless() { return 888; } } public class Stub { public void Foo(int stub) { } }

И следующее наполение метода Main:

class Program { static void Main(string[] args) { Test2 fake = new CustomStructWithLayout { Test2 = new Test2(), Test1 = new Test1() }.Test2; Stub bar = new Stub(); int param = 55555; bar.Foo(param); fake.Useless(); Console.Read(); } }

Как вы догадываетесь, по опыту предыдущей статьи, будет вызван метод Useless(int j) типа Test1.

Внимательный читатель, полагаю, уже ответил на этот вопрос. Но что будет выведено? На консоль выведено «55555».

Но давайте все же глянем на фрагменты сгенерированного кода

mov ecx, [ebp-0x20] mov edx, [ebp-0x10] cmp [ecx], ecx call Stub.Foo(Int32) nop mov ecx, [ebp-0x1c] mov eax, [ecx] mov eax, [eax+0x28] call dword [eax+0x10]

Думаю, вы узнали шаблон вызова виртуального метода, он начинается после L00cc: nop. Как мы видим, в ecx ожидаемо записывается адрес экзепляра, на котором вызвается метод. Но т.к. мы вызываем якобы метод типа Test2, который не имеет параметров, то в edx ничего не записывается. Однако до этого был вызван метод, которому передавался параметр как раз через регистр edx, соответсвенно, значение в нем и осталось. и его мы можем наблюдать в окне вывода.

Я специально использовал значимый тип. Есть еще один интересный нюанс. Но тип параметра метода Useless не изменять. Предлагаю попробовать заменить тип параметра метода Foo типа Stub на любой ссылочный тип, например, строку. Ниже можете увидеть результат на моей машине с некоторыми проясняющими элементами: WinDBG и Калькулятором 🙂


Картинка кликабельна

В окне вывода выводится адрес ссылочного типа в десятичной системе счисления

Итог

Освежили в памяти знания о вызове методов при помощи соглашения fastcall и тут же воспользовались чудным регистром edx для передачи параметра в 2 метода за раз. Также наплевали на все типы и вспомнив, что все есть лишь байты без труда получили адрес объекта без использования указателей и unsafe кода. Далее планирую использовать полученный адрес для еще более неприменимых целей!

Спасибо за внимание!

S. P. Код на C# можно найти по ссылке


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Вышел PostgreSQL 11

Специальный выпуск POSTGRESSO, посвященный выходу официального релиза версии 11. На улице PostgreSQL праздник. После четырех beta вышла PostgreSQL 11 General Availability, то есть официальная версия. В анонсе есть даже приветственное слово Брюса Момджана: «готовя этот релиз, сообщество особенно заботилось о ...

Вольтметр для батареек: карманный гаджет для смартфона с «крокодильчиками»

Компания FTlab во многом известна как производитель мобильных полупроводниковых датчиков с разъемом Jack, каждый из которых определяет либо уровень ультрафиолета, либо температуру и влажность, либо электромагнитное излучение и даже уровень радиации. Устройства имеют довольно ограниченную применимость из-за характеристик, и скорее ...