| Дышит река, Кости стрелка Встретят закат у рудника. Дети войны, вам, Красные сны, вам, Тени стены, вам. Он мертвый, он мертвый, Мертвый охотник на мертвых поднимет ружье. Аукцыон, "Охотник", альбом "Как я стал предателем" 1989 |
Итак, продолжим. На сей раз я наваял приложение, использующее несколько более продвинутые технологии, предоставляемые Delphi - exceptions handling ( перехват исключений ), virtual & dynamic функции, обработку формой сообщений Windows, производные классы и загрузку строковых ресурсов из реестра. Исходный код моей программы мог бы выглядеть как-нибудь так:
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TRPEnum = ( RP_One, RP_Two, RP_Tree );
TRPEnumSet = set of TRPEnum;
TRPException = class(Exception)
private
RP_Array: array[7..9] of string;
Code: TRPEnumSet;
public
Procedure Old_one_virtual; virtual;
Procedure Old_one_dynamic; dynamic;
Constructor Create;
Destructor Destroy; override;
end;
TRPExceptionChild = class(TRPException)
Procedure Old_one_virtual; override;
Procedure Old_one_dynamic; override;
end;
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
FDesignedWidth, FDesignedHeight: Integer;
procedure BuggyOne;
Procedure WMSizing( var Message: TMessage ); message WM_SIZING;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
resourcestring
BuggyOneCaption = 'BuggyOne';
MalformedException = 'Malformed exception';
(* TRPException *)
Constructor TRPException.Create;
begin
Application.MessageBox('Create', 'TRPException', ID_OK);
inherited Create('BuggyOne object');
end;
Destructor TRPException.Destroy;
begin
Application.MessageBox('Destroy', 'TRPException', ID_OK);
Inherited;
end;
Procedure TRPException.Old_one_virtual;
begin
Application.MessageBox('Old_one_virtual','TRPException', ID_OK);
end;
Procedure TRPException.Old_one_dynamic;
begin
Application.MessageBox('Old_one_dynamic','TRPException', ID_OK);
end;
(* TRPExceptionChild *)
Procedure TRPExceptionChild.Old_one_virtual;
begin
Application.MessageBox('Old_one_virtual','TRPExceptionChild', ID_OK);
end;
Procedure TRPExceptionChild.Old_one_dynamic;
begin
Application.MessageBox('Old_one_dynamic','TRPExceptionChild', ID_OK);
end;
(* TForm1 *)
procedure TForm1.BuggyOne;
var
RP_E: TRPExceptionChild;
N: Integer;
begin
MessageDlg(BuggyOneCaption,mtConfirmation,[mbOk],0);
try
RP_E := TRPExceptionChild.Create;
RP_E.Code := [RP_One];
RP_E.RP_Array[7] := 'Seven';
N := 9;
RP_E.RP_Array[8] := 'Eight';
RP_E.RP_Array[N] := 'Nine inch nails';
RP_E.Code := RP_E.Code + [RP_Two];
Raise RP_E;
MessageDlg('Not will showed at the end',mtConfirmation,[mbOk],0);
finally
MessageDlg('In finally part',mtConfirmation,[mbOk],0);
end;
MessageDlg('Not will showed at the end',mtConfirmation,[mbOk],0);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
MessageDlg('Button1Click',mtConfirmation,[mbOk],0);
try
BuggyOne;
except
on E:TRPException do
begin
MessageDlg('Button1Click in exception block',mtConfirmation,[mbOk],0);
E.Old_one_virtual;
E.Old_one_dynamic;
end;
on E:TRPExceptionChild do
begin
MessageDlg(MalformedException,mtConfirmation,[mbOk],0);
end;
end;
MessageDlg('Button1Click at the end',mtConfirmation,[mbOk],0);
end;
Procedure TForm1.WMSizing( var Message: TMessage );
var
PRect : ^TRect;
Begin
PRect := Pointer (Message . LParam );
if PRect^. Right - PRect^. Left < FDesignedWidth then
begin
if Message.WParam in [ WMSZ_BOTTOMLEFT, WMSZ_LEFT, WMSZ_TOPLEFT ]
then
PRect^.Left := PRect^ . Right - FDesignedWidth
else
PRect^.Right := PRect^ . Left + FDesignedWidth;
end;
if PRect^ . Bottom - Prect^.Top < FDesignedHeight then
begin
if Message . WParam in [ WMSZ_TOP, WMSZ_TOPLEFT, WMSZ_TOPRIGHT ]
then
PRect^.Top := PRect^ . Bottom - FDesignedHeight
else
PRect^. Bottom := PRect^ . Top + FDesignedHeight;
end;
End;
procedure TForm1.FormCreate(Sender: TObject);
begin
FDesignedWidth := Width;
FDesignedHeight := Height;
MessageDlg('FormCreate',mtConfirmation,[mbOk],0);
end;
Не вершина программистского мастерства,
конечно, но для наших целей вполне годится.
Итак, запустим дизассемблер ( я использовал
IDA 3.8b ) и не забудьте применить файл сигнатур
для библиотеки VCL версии 4 ( d4vcl ) - в моём
случае IDA опознала 2172 функции.
| Смещение | тип | описание |
| 0 | WORD | Размер N hashа |
| 2 | WORD | N слов - индексов dynamic методов |
| N * 2 + 2 | DWORD | N указателей на функции |
Что ещё более интересно, в нашём
случае индекс единственной функции - WMSizing
0x214. Если Вы посмотрите в файле заголовков
messages.pas, 0x214 ( eq 532 ) есть значение сообщения
WM_SIZING. В Borlandе, видимо, простые парни
работают...
Итак, сейчас у нас есть более полное
описание RTTI, я позволю себе повторить его
здесь полностью:
| смещение | тип | описание |
| 0 | DWORD | указатель на VTBL |
| 4 | DWORD | значение не выяснено (vmtIntfTable) |
| 8 | DWORD | значение не выяснено (vmtAutoTable) |
| Ch | DWORD | значение не выяснено (vmtInitTable) |
| 10h | DWORD | указатель на список наследований |
| 14h | DWORD | указатель на компоненты, которыми владеет данный класс |
| 18h | DWORD | указатель на массив обработчиков событий |
| 1Ch | DWORD | указатель на hash dynamic методов |
| 20h | DWORD | указатель на Pascal-строку - имя класса |
| 24h | DWORD | размер класса |
| 28h | DWORD | указатель на структуру RTTI класса-предка данного класса |
| 2Ch | DWORD | указатель на метод SafeCallException |
| 30h | DWORD | указатель на метод AfterConstruction |
| 34h | DWORD | указатель на метод BeforeDestruction |
| 38h | DWORD | указатель на метод Dispatch |
| 3Ch | DWORD | указатель на метод DefaultHandler |
| 40h | DWORD | указатель на метод NewInstance |
| 44h | DWORD | указатель на метод FreeInstance |
| 48h | DWORD | указатель на метод Destroy |
| 4Ch | DWORDs | начало VTBL |
BuggyOne proc near
var_4 = dword ptr -4
push ebp
mov ebp, esp
push 0 ; инициализация в 0 var_4
push ebx ; сохранить ebx
push esi ; и esi
xor eax, eax
push ebp ; и ещё ebp
push offset loc_0_44064F ; поместим в стек адрес
; finally кода
push dword ptr fs:[eax] ; и прежнее значение стека
; обработки исключений
mov fs:[eax], esp ; в стек обработки исключений
; помещается указатель на текущее значение стека
push 0
lea edx, [ebp+var_4] ; загрузим в var_4 строку из
mov eax, offset off_0_440368 ; ресурсов
call @LoadResString
...
loc_0_440646:
lea eax, [ebp+var_4] ; очистить строку в var_4
call @@LStrClr ; ::`intcls'::LStrClr
retn
; ------------------------------------------------------------------
loc_0_44064F:
jmp @@HandleFinally ; ::`intcls'::HandleFinally
; ------------------------------------------------------------------
jmp short loc_0_440646
Обратите внимание на две вещи:
@@HandleFinally:
mov eax, [esp+4]
mov edx, [esp+8]
test dword ptr [eax+4], 6
jz short loc_0_403294
mov ecx, [edx+4] ; адрес перехода на HandleFinally
mov dword ptr [edx+4], offset loc_0_403294
push ebx
push esi
push edi
push ebp
mov ebp, [edx+8]
add ecx, 5 ; добавим к нему 5
call @System@_16583 ; System::_16583
call ecx ; и вызовем как функцию
pop ebp
pop edi
pop esi
pop ebx
loc_0_403294:
mov eax, 1
retn
На момент вылета на этот код в fs:[0] и eax
содержится указатель стека, в котором
находятся ранее занесённые в него (
смотрите начало процедуры BuggyOne; также я
привык изображать вершину стека сверху, а
не как оно есть на самом деле ):
[eax + 4] прежнее значение стека в fs:[0]
| [eax + 8] указатель на инструкцию
перехода к HandleFinally
| [eax + 0xC] ebp | |
type
PResStringRec = ^TResStringRec;
TResStringRec = record
Module: ^Longint;
Identifier: Integer;
end;
function LoadResString(ResStringRec: PResStringRec): string;
Module - handler загруженного модуля,
содержащего в себе ресурс. Для нашей
программы это hInstance самого приложения (
поскольку главная форма находится в том же
модуле, что и объект TApplication ).
off_0_440368 dd offset dword_0_4424D8 ; hModule приложения
dd 0FF5Dh ; Identifier
Число 0xFF5D = 65373 < 65536, так что наша строка
идентифицируется по числовому значению.
Посмотрим ресурсы моей программы в
редакторе ресурсов Restorator ( кстати, весьма
рекомендую эту программу для исследований
приложений на Delphi - она умеет показывать
описание Delphi-форм ! ). Наша строка нашлась в
секции string tables под номером секции 4086,
смещение -3. Как это соотносится с ранее
найденным значением идентификатора ? Очень
просто:
65373 = 4086 * 16 - 3;Всё гениальное просто ( однако не всё простое гениально ).
xor eax, eax
push ebp
push offset loc_0_44061D ; новый finally handler
push dword ptr fs:[eax]
mov fs:[eax], esp
mov dl, 1
mov eax, ds:off_0_44016C ; ptr to TRPExceptionChild RTTI
call sub_0_440378 ; TRPExceptionChild::Create
mov ebx, eax
mov al, ds:byte_0_440660 ; db 1 eq RP_One из TRPEnum
mov [ebx+18h], al
lea eax, [ebx+0Ch]
mov edx, offset aSeven ; "Seven"
call @@LStrAsg ; ::`intcls'::LStrAsg
mov esi, 9
lea eax, [ebx+10h]
mov edx, offset aEight ; "Eight"
call @@LStrAsg ; ::`intcls'::LStrAsg
lea eax, [ebx+esi*4-10h]
mov edx, offset aNineInchNails ; "Nine inch nails"
call @@LStrAsg ; ::`intcls'::LStrAsg
mov al, [ebx+18h]
or al, ds:byte_0_44069C ; db 2 eq RP_Two из TRPEnum
mov [ebx+18h], al
mov eax, ebx
call @@RaiseExcept ; ::`intcls'::RaiseExcept
...
loc_0_44061D:
jmp @@HandleFinally
; ----------------------------------------
jmp short loc_0_440607
...
loc_0_440607:
push 0
mov cx, ds:word_0_44065C
mov dl, 3
mov eax, offset aInFinallyPart
call @MessageDlg
retn
Дальше совсем просто. Инициализируется
новый обработчик finally части, при этом
указатель на старое значение стека также
помещается в стек. Далее вызывается
конструктор TRPExceptionChild::Create - первым
аргументом ему передаётся указатель на RTTI
класса TRPExceptionChild, а вторым ( в регистре dl,
я не знаю для чего ) 1 - указатель на
созданный экземпляр класса возвращается в
регистре eax, и затем пересылается в ebx,
который используется в дальнейшем как
базовый регистр. Члену Code присваивается
значение RP_One ( eq 1 ) из набора TRPEnum. Можно
заметить, что Code расположена в классе TRPException
по смещению 0x18h. Затем идёт присваивание
значений массиву строк - массив начинается
по смещению 0xC. Довольно непонятно выглядит
присваивание последнему ( 9ому элементу
массива ): он должен быть расположен по
смещению 0xC + (3 - 1) * 4 = 0x14; 9 * 4 - 0x10 даёт то же
самое 0x14, но какова логика ! Затем к нашему
набору Code добавляется RP_Two ( eq 2 ). Потом
вызывается процедура RaiseExcept с единственным
аргументом в eax - адресом нашего класса.
...
push offset loc_0_440728
push dword ptr fs:[edx]
mov fs:[edx], esp
mov eax, ebx
call BuggyOne ; процедура, генерирующая исключение
xor eax, eax
pop edx
pop ecx
pop ecx
mov fs:[eax], edx
jmp short loc_0_440790
; --------------------------------------------------------
loc_0_440728:
jmp @@HandleOnException ; ::`intcls'::HandleOnExceptions
; --------------------------------------------------------
dd 2 ; размер фильтров исключений
dd offset off_0_4400F4 ; адрес RTTI TRPException
dd offset loc_0_440741 ; адрес код для TRPException
dd offset off_0_44016C ; TRPExceptionChild
dd offset loc_0_44076B ; On TRPExceptionChild
; ----------------------------------------------------------------
loc_0_440741:
mov ebx, eax
push 0
mov cx, ds:word_0_4407C8
mov dl, 3
mov eax, offset aButton1clickIn
; строка "Button1Click in exception block"
call @MessageDlg
mov eax, ebx
mov edx, [eax] ; вызов TRPExceptionChild::Old_one_virtual
call dword ptr [edx]
mov eax, ebx
mov bx, 0FFFFh ; вызов TRPExceptionChild::Old_one_dynamic
; имеет индекс 0xFFFF
call @@CallDynaInst ; ::`intcls'::CallDynaInst
jmp short loc_0_44078B
Здесь можно увидеть в действии механизм
фильтрации и обработки исключений. Опять в
fs:[0] помещается указатель на стек, но на сей
раз в него помещён адрес инструкции
перехода к процедуре обработке исключений HandleOnExceptions.
Следом за ней расположен массив фильтров
исключений. Он имеет весьма незатейливую
структуру:
| Смещение | тип | описание |
| 0 | DWORD | Размер N массива фильтров исключений |
| 4 | DWORD | Указатель на RTTI класса - объекта исключение |
| 8 | DWORD | Указатель на код, вызываемый при исключении этого класса |
| 4 + M * 4 | DWORD | Указатель на M-ную RTTI класса - объекта исключение |
| 8 + M * 4 | DWORD | Указатель на код, вызываемый при исключении M-ного класса |
Поскольку я человек ленивый ( лень - двигатель прогресса ) и мне совершенно не хотелось вручную сообщать IDA Pro, что это не просто нечто бесформенное, а самая что ни наесть структура RTTI ( при этом ещё мучительно вспоминая, чего там идёт под каким смещением ), я написал небольшой script на IDC, который позволяет мне иметь немного свободного времени для прямых обязанностей сисадмина, а именно - для чтения newsов и взлома программ...
/*
* This script deal with Delphi RTTI structures
*
* Red Plait, 23-VIII-1999
*/
#include <idc.idc>
// makes dword and offset to data
static MakeOffset(adr)
{
auto ref_adr;
MakeUnkn(adr,0);
MakeUnkn(adr+1,0);
MakeUnkn(adr+2,0);
MakeUnkn(adr+3,0);
MakeDword(adr);
ref_adr = Dword(adr);
if ( ref_adr != 0 )
add_dref(adr, ref_adr, 0);
}
// makes dword and offset to a function
static MakeFOffset(adr,string)
{
auto ref_adr, func_name;
MakeUnkn(adr,0);
MakeUnkn(adr+1,0);
MakeUnkn(adr+2,0);
MakeUnkn(adr+3,0);
MakeDword(adr);
ref_adr = Dword(adr);
if ( ref_adr != 0 )
{
MakeFunction(ref_adr, BADADDR);
MakeName(ref_adr,string);
add_dref(adr,ref_adr,0);
}
}
// makes simple string
static do_Str(adr,len)
{
auto count;
for ( count = 0; count < len; count++ )
MakeUnkn(adr + count,0);
MakeStr(adr, adr+len);
}
// makes Pascal-style string
static makePStr(adr)
{
auto len;
MakeUnkn(adr,0);
MakeByte(adr);
len = Byte(adr);
do_Str(adr+1,len);
return len + 1;
}
// extract pascal-style string
static getPStr(adr)
{
auto len, res, c;
len = Byte(adr++);
res = "";
for ( ; len; len-- )
{
c = Byte(adr++);
res = res + c;
}
return res;
}
// returns name of class of this RTTI
static getRTTIName(adr)
{
auto ptr;
ptr = Dword(adr+0x20);
if ( ptr != 0 )
return getPStr(ptr);
else
return "";
}
// processing owned components list
static processOwned(adr)
{
auto count, str_len, comp_count, rtti_base;
MakeUnkn(adr,0);
MakeUnkn(adr+1,0);
MakeWord(adr);
comp_count = Word(adr); /* count of RTTI array */
adr = adr + 2;
MakeOffset(adr);
rtti_base = Dword(adr); /* offset to array of RTTI */
adr = adr + 4;
/* process RTTI array */
MakeUnkn(rtti_base,0);
MakeUnkn(rtti_base+1,0);
MakeWord(rtti_base); /* size of array */
count = Word(rtti_base);
rtti_base = rtti_base + 2;
for ( str_len = 0; str_len < count; str_len++ )
{
MakeOffset(rtti_base + str_len * 4);
}
/* process each of owned to form components */
for ( count = 0; count < comp_count; count++ )
{
// offset in owners class
MakeUnkn(adr,0);
MakeUnkn(adr+1,0);
MakeWord(adr);
str_len = Word(adr);
MakeComm(adr, "Offset 0x" + ltoa(str_len,0x10) );
adr = adr + 2;
// unknow word
MakeUnkn(adr,0);
MakeUnkn(adr+1,0);
MakeWord(adr);
adr = adr + 2;
// index in RTTI array
MakeUnkn(adr,0);
MakeUnkn(adr+1,0);
MakeWord(adr);
str_len = Word(adr);
MakeComm(adr, "Type: " + getRTTIName(Dword(rtti_base + str_len*4)) );
adr = adr + 2;
// pascal string - name of component
MakeUnkn(adr,0);
str_len = Byte(adr);
adr = adr + 1;
do_Str(adr,str_len);
adr = adr + str_len;
}
}
// process events handlers list
static processHandlers(adr)
{
auto count, str_len, f_addr;
MakeUnkn(adr,0);
MakeUnkn(adr+1,0);
MakeWord(adr);
MakeComm(adr,"Handlers count");
count = Word(adr);
adr = adr + 2;
for ( ; count; count-- )
{
// unknown dword
MakeUnkn(adr,0);
MakeUnkn(adr+1,0);
MakeWord(adr);
adr = adr + 2;
// offset to function - handler
f_addr = Dword(adr);
MakeOffset(adr);
adr = adr + 4;
// Name of handler
if ( f_addr != 0 )
{
MakeCode(f_addr);
MakeFunction(f_addr, BADADDR);
MakeName(f_addr, getPStr(adr));
}
adr = adr + makePStr(adr);
}
}
// process inherited list first element ( may be recursive ? )
// returns pointer to next parent`s struct
static processParent(adr)
{
auto str_len;
auto res;
res = 0;
// 1st byte - unknown
MakeUnkn(adr,0);
MakeByte(adr);
adr = adr + 1;
// next - Pascal string - name of class
adr = adr + makePStr(adr);
// VTBL pointer
MakeOffset(adr);
adr = adr + 4;
// next - pointer to pointer to next this struct :-)
MakeOffset(adr);
str_len = Dword(adr);
if ( str_len != 0 )
{
MakeOffset(str_len);
res = Dword(str_len);
}
adr = adr + 4;
// WORD - unknown
MakeUnkn(adr,0);
MakeUnkn(adr+1,0);
MakeWord(adr);
adr = adr + 2;
// next - name of Unit name
makePStr(adr);
return res;
}
// process dynamic methods table
static processDynamic(adr)
{
auto count, base, i, old_comm;
MakeUnkn(adr,0);
MakeUnkn(adr+1,0);
MakeWord(adr);
count = Word(adr);
MakeComm(adr,"Count of dynamic methods " + ltoa(count,10) );
adr = adr + 2;
base = adr + 2 * count;
for ( i = 0; i < count; i++ )
{
MakeUnkn(adr,0);
MakeUnkn(adr+1,0);
MakeWord(adr);
MakeOffset(base + 4 * i);
old_comm = Comment(base + 4 * i);
if ( old_comm != "" )
MakeComm(base + 4 * i, "Dynamic 0x" + ltoa(Word(adr),0x10) + ", " + old_comm );
else
MakeComm(base + 4 * i, "Dynamic 0x" + ltoa(Word(adr),0x10) );
adr = adr + 2;
}
return count;
}
// makes tricky VTBL entries
static makeF2Offset(adr,name)
{
auto comm,ref_adr;
MakeOffset(adr);
ref_adr = Dword(adr);
if ( ref_adr != 0 )
add_dref(adr,ref_adr,0);
comm = Comment(adr);
if ( comm != "" )
MakeComm(adr, comm + ", " + name);
else
MakeComm(adr, name);
}
// main function - process RTTI structure
static processRTTI(adr)
{
auto count;
auto res;
auto my_name;
my_name = "";
// first DWORD - VTBL pointer
MakeOffset(adr);
// three next DWORD is unknown
MakeOffset(adr+4);
MakeOffset(adr+8);
MakeOffset(adr+0xc);
// list of parents
MakeOffset(adr+0x10);
count = Dword(adr+0x10);
if ( count != 0 ) // also process first parent for this class
processParent(count);
// 0x14 DWORD - owned components
MakeOffset(adr+0x14);
count = Dword(adr+0x14);
if ( count != 0 )
processOwned(count);
// 0x18 DWORD - event handlers list
MakeOffset(adr+0x18);
count = Dword(adr+0x18);
if ( count != 0 )
processHandlers(count);
// 0x1c DWORD - pointer to dynamic functions list
MakeOffset(adr+0x1c);
count = Dword(adr+0x1c);
if ( count != 0 )
{
count = processDynamic(count);
MakeComm(adr+0x1c, ltoa(count,10) + " dynamic method(s)");
}
// 0x20 DWORD - pointer to class name
MakeOffset(adr+0x20);
count = Dword(adr+0x20);
if ( count != 0 )
{
makePStr(count);
my_name = getPStr(count);
MakeComm(adr+0x20, "Name: " + my_name );
}
// 0x24 DWORD - size of class
MakeUnkn(adr+0x24,0);
MakeUnkn(adr+0x25,0);
MakeUnkn(adr+0x26,0);
MakeUnkn(adr+0x27,0);
MakeDword(adr+0x24);
MakeComm(adr+0x24,"Size of class");
// 0x28 - pointer to parent`s RTTI struct
MakeOffset(adr+0x28);
res = Dword(adr+0x28);
MakeComm(adr+0x28,"Parent`s class");
// 0x2c SafeCallException
makeF2Offset(adr+0x2c,my_name + "::SafeCallException");
// 0x30 AfterConstruction
makeF2Offset(adr+0x30,my_name + "::AfterConstruction");
// 0x34 BeforeConstruction
makeF2Offset(adr+0x34,my_name + "::BeforeConstruction");
// 0x38 Dispatch
makeF2Offset(adr+0x38,my_name + "::Dispatch");
// 0x3C DefaultHandler
makeF2Offset(adr+0x3c,my_name + "::DefaultHandler");
// 0x40 NewInstance
makeF2Offset(adr+0x40,my_name + "::NewInstance");
// 0x44 FreeInstance
makeF2Offset(adr+0x44,my_name + "::FreeInstance");
// 0x48 Destroy
makeF2Offset(adr+0x48,my_name + "::Destroy");
return res;
}
Пояснения по каждой функции:MakeOffset создаёт смещение по
указанному адресу adr. Иногда IDA бывает
упряма, и настаивает, что по этому адресу
вовсе не смещение, а, скажем, data - четырёх
вызовов MakeUnkn обычно бывает
достаточно, чтобы изменить её мнение
| MakeFOffset - аналогично MakeOffset, но
только создаёт смещение на функцию,
которую называет string. Warning: не
проверяется результат MakeFunction,
поэтому функция может быть не совсем
правильной. В любом случае, это нужно
проверять обычно.
| do_Str - помечает len байт с адреса adr
как строку
| makePStr - создаёт pascal-строку по адресу adr.
Возвращает её длину, включая сам байт
длины.
| getPStr - возвращает значение pascal-строки
по адресу adr.
| getRTTIName - возвращает имя класса по его
RTTI, расположенной по адресу adr
| processOwned - обрабатывает список
компонентов, принадлежащих данному
компоненту. Сам список начинается с
адреса adr
| processHandlers - обрабатывает список
функций-обработчиков событий. Сам список
начинается с адреса adr. Warning: так
как имеет место попытка назвать функцию
так же, как она называлась в этом классе,
имя может быть неуникально ( вспомните,
сколько раз у Вас были функции с именем Button1Click
в разных классах )
| processParent - обрабатывает один элемент в
списке наследований по адресу adr. Это
рекурсивная структура, но её окончание
может быть обозначено по-разному - либо
как Nil, либо как две структуры TObject,
ссылающиеся друг на друга. На всякий
случай возвращается ссылка на следующий
элемент.
| processDynamic - обрабатывает hash
динамических методов по адресу adr. Warning:
несмотря на то, что этот метод пытается
сохранить ранее данные комментарии для
указателя на каждую dynamic функцию, похоже,
что в IDA Pro есть bug, из-за которого нельзя
извлечь комментарии для опознанных с
помощью сигнатур функций ( который по
умолчанию имеют тёмно-коричневый цвет,
скажем, TFormCustom::WMPaint ). Вызов Comment на таких
комментариях возвращает пустую строку.
Возвращается число динамических функций.
| makeF2Offset - вспомогательная функция,
служит по пометки служебных функций ( с
отрицательным индексом в VTBL ) и
добавления к ним комментария. Адрес
функции передаётся в adr, строка
комментария в name
| processRTTI - собственно, самая главная
функция, собирающая все остальные -
обрабатывает структуру RTTI по адресу adr | |