381просмотров
12 января 2026 г.
Score: 419
IDA и CXX Так исторически сложилось, что работа с классами в IDA основана на работе со структурами.
Виртуальная таблица функций? Структура.
Поля класса? Структура. И в целом, на базовом уровне этого достаточно. Но когда в коде появляется наследование, упаси боже, если множественное, разнообразные интерфейсы, а методы начинают принимать указатель на базовый класс или на промежуточный в иерархии, реверс-инженер может оказаться в ситуации, когда у него есть множество структур, поля которых на самом деле принадлежат одному и тому же типу, то есть классу. Самое неприятное последствие, имя, восстановленное для одного из полей, не будет проставлено в других структурах. Имена можно продублировать вручную в другие структуры, если их не так много, но можно поступить проще. Здесь я хочу поделиться с вами моим workaround’ом о том, как описывать в IDA структуры, соответствующие классам, которые могут быть унаследованы, так, чтобы имена полей (и, как следствие, XREF’ы) агрегировались корректно. Для этого достаточно понимать, как компилятор формирует memory layout класса.
Класс это VFTable + поля. Соответственно, мы можем разделить структуру, описывающую класс, на две структуры: VFTable и Fields, и объявить их отдельно. Рассмотрим на примере базового класса для каждого типа ресурса, с которым может манипулировать DWM. Класс CResource из dwmcore.dll.
struct My_CResource { My_CResource__VTable vtable; My_CResource__Fields f;
}; struct My_CResource__Fields { ...
}; struct My_CResource__VTable { ...
}; Как можно видеть, класс CResource объявлен как структура, комбинирующая в себе VFTable и поля. Теперь, когда нам нужно объявить класс, унаследованный от CResource, необходимо сделать следующее. Рассмотрим пример класса CVisual из той же dwmcore.dll. struct My_CVisual { My_CVisual__VTable vtable; My_CVisual__Fields f;
}; struct My_CVisual__Fields { My_CResource__Fields f; ...
}; Как можно видеть, класс CVisual также объявлен как структура, комбинирующая в себе VFTable и поля. Обратите внимание на то, как объявлены поля класса CVisual: первое поле имеет тип My_CResource__Fields. Это, по сути, «переносит» поля базового класса в то место в памяти, где код ожидает их увидеть, и при этом сохраняет единую точку агрегации имён и типов полей базового класса. Для закрепления рассмотрим ещё один уровень, класс CSpriteVisual, который наследуется от CVisual. Он объявлен следующим образом:
struct My_CSpriteVisual { My_CSpriteVisual__VTable *vtable; My_CSpriteVisual__Fields f;
}; struct My_CSpriteVisual__Fields { My_CVisual__Fields f; ...
}; В объявлении CSpriteVisual мы переносим не только поля CVisual, но и, транзитивно, поля CResource. #ida #cxx #tip