请见下面的更新
众所周知,帮助程序可以破解私有可见性。因此,类助手可以看到私有成员。然而,这种行为并不扩展到静态成员,因此TCharacter.IsLatin1
在声明它的单元之外是无法访问的(通过公平手段)。
那么,不公平手段呢?好吧,TCharacter
的一些公共方法确实调用了IsLatin1
。尽管IsLatin1
被声明为inline
,但这些方法似乎是编译为调用语句,而不是内联代码。也许这是因为它们调用发生在同一个单元或同一个类型中,但内联引擎无法内联。
无论如何,我想说的是,在运行时,您可以反汇编其中一个这样的调用。举个例子,考虑IsControl
:
class function TCharacter.IsControl(C: Char): Boolean;
begin
if IsLatin1(C) then
Result := InternalGetLatin1Category(C) = TUnicodeCategory.ucControl
else
Result := InternalGetUnicodeCategory(UCS4Char(C)) = TUnicodeCategory.ucControl;
end;
它的第一个操作是调用
IsLatin1
。编译后的代码如下:
System.Character.pas.517:
00411135 C3 ret
00411136 8BC0 mov eax,eax
TCharacter.IsControl:
00411138 53 push ebx
00411139 8BD8 mov ebx,eax
System.Character.pas.533:
0041113B 8BC3 mov eax,ebx
0041113D E852FFFFFF call TCharacter.IsLatin1
00411142 84C0 test al,al
00411144 740F jz $00411155
所以,你可以这样做:
- 获取
TCharacter.IsControl
的地址。
- 反汇编该地址处的代码,直到找到第一个
call
指令。
- 解码
call
指令以查找目标地址,这就是IsLatin1
所在的位置。
我并不是在为
IsLatin1
进行宣传。它是一个非常简单的函数,不会发生变化,因此重新实现它肯定更好。但是对于更复杂的情况,可以使用这种方法。
我也不声称具有原创性。我从madExcept源代码中学到了这种技术。
好吧,@LU RD聪明地找到了证明我错误的方法。恭喜他。虽然我关于
static
方法的说法是准确的,但@LU RD使用了一个非常熟练的技巧,引入了一个非静态类方法,并通过这种方式破解了私有成员。
我想进一步展示如何使用两个辅助程序来使用原始名称公开功能:
unit CharacterCracker;
interface
uses
System.Character;
type
TCharacterHelper = class helper for TCharacter
public
class function IsLatin1(C: Char): Boolean; static; inline;
end;
implementation
type
TCharacterCracker = class helper for TCharacter
public
class function IsLatin1Cracker(C: Char): Boolean; inline;
end;
class function TCharacterCracker.IsLatin1Cracker(C: Char): Boolean;
begin
Result := TCharacter.IsLatin1(C);
end;
class function TCharacterHelper.IsLatin1(C: Char): Boolean;
begin
Result := TCharacter.IsLatin1Cracker(C);
end;
end.
你可以使用这个单元,而在单元外唯一激活的帮助者是在接口部分声明的那个。这意味着你可以写出如下代码:
{$APPTYPE CONSOLE}
uses
System.Character,
CharacterCracker in 'CharacterCracker.pas';
var
c: Char;
begin
c := #42;
Writeln(TCharacter.IsLatin1(c));
c := #666;
Writeln(TCharacter.IsLatin1(c));
Readln;
end.