проблема
SearchTickCount
- это глобальная переменная (уровня блока), объявленная в разделе реализации модуля, к которой нельзя обращаться за пределами этого модуля. У вас возникла бы та же проблема, если бы вы работали с Delphi, а не с C ++ Builder.
Нормальные решения
- Подкласс
TDBLookupControl
, переопределите ProcessSearchKey()
и убедитесь, что он использует ваш SearchTickCount
, тот, который легко доступен. К счастью ProcessSearchKey()
является виртуальным, теоретически это должно работать, но на практике код зависит от FListField
, это частное поле, поэтому мы вернемся к квадрату 1.
- Скопируйте весь
TDBLookupControl
на свой TMyDBLookupControl
и убедитесь, что вы можете получить доступ к SearchTickCount
. Это будет окончательно работать.
HACKY решение
Конечно, хаки гораздо веселее. Процессор не имеет проблем с поиском SearchTickCount
, потому что адрес закодирован в инструкции ASM, которые составляют код ProcessSearchKey's
. То, что процессор может прочитать, мы можем прочитать.
Оценивая код для метода ProcessSearchKey
, он использует только одну глобальную переменную (SearchTickCount
) и использует ее в двух местах. Первый в этом тесте:
if TickCount - SearchTickCount > 2000 then
тогда в этой инструкции:
SearchTickCount := TickCount;
Если вы посмотрите на список разборок этой подпрограммы, доступ к глобальной переменной будет легко обнаружен, потому что он дает адрес переменной в квадратных скобках, без другого квалификатора. Чтобы if
работал, компилятор делает что-то вроде этого:
SUB EAX, [$000000]
Для присваивания компилятор делает что-то вроде этого:
MOV [$000000], EAX // or ESI on Delphi 7 with debug enabled
Если вы посмотрите слева от инструкции на ассемблере, вы можете легко увидеть фактический код операции в шестнадцатеричной записи. Например, SUB EAX, [$ 000000] выглядит так:
2B0500000000
Мое хакерское решение использует это. Я получаю адрес самой процедуры (TDBLookupControl.ProcessSearchKey
), сканирую код в поисках кода операции (2B 05
) и получаю адрес. Вот и все, и это работает.
Конечно, это имеет потенциальные проблемы. Это зависит от кода, скомпилированного с этими точными регистрами (EAX
в моем примере). Компилятор может выбирать разные регистры. Я тестировал и Delphi7, и Delphi 2010, с кодом, скомпилированным для Debug, и скомпилированным без Debug. Во всех 4 случаях компилятор решил использовать EAX
для инструкции SUB
, а в 3/4 - ESI
в качестве регистра для инструкции MOV
. Из-за этого мой код ищет только инструкцию SUB
.
С другой стороны, если код работает один раз , код работает каждый раз. Код не меняется после выпуска, поэтому, если вы сможете правильно протестировать на компьютере разработчика, вы не получите неприятных AV на компьютере клиента. Но используйте на свой страх и риск, это ведь взлом!
Вот код:
unit Unit2;
interface
uses DbCtrls;
function GetSearchTickCountPointer: PInteger;
implementation
type
THackDbLookupControl = class(TDBLookupControl); // Hack to get address of protected member
TInstructionHack = packed record
OpCodePrefix: Word;
OpCodeAddress: PInteger;
end;
PInstructionHack = ^TInstructionHack;
function GetSearchTickCountPointer: PInteger;
var P: PInstructionHack;
N: Integer;
begin
P := @THackDbLookupControl.ProcessSearchKey;
N := 0; // Sentinel counter, so we don't look for the opcode for ever
while N < 2000 do
begin
if P.OpCodePrefix = $052B then // Looking for SUB EAX, [SearchTickCount]
begin
Result := P.OpCodeAddress;
Exit;
end;
Inc(N);
P := PInstructionHack(Cardinal(P)+1); // Move pointer 1 byte
end;
Result := nil;
end;
end.
Вы используете хакерскую версию, подобную этой:
var P: PInteger;
begin
P := GetSearchTickCountPointer;
if Assigned(P) then
P^ := 1; // change SearchTickCount value!
end;