如何在Delphi EXE中加密或隐藏字符串?

14

我目前正在使用Delphi开发一个应用程序,在其中必须隐藏(混淆)源代码中的字符串,例如str := '示例字符串'
为什么?因为如果我在文本编辑器中打开EXE并搜索示例字符串,我会在第二个位置找到该字符串...

我尝试使用基本的十六进制编码,例如#$65#$78#$61#$6d#$70#$6c#$65 ,但它会在编译时被重新转换为明文。
我寻找了压缩工具,但这不是最好的解决方案(PECompact可能被检测为误报病毒,UPX太容易被反UPX...)。我更喜欢在我的内部代码中寻找想法...

请大家指点迷津。


它不是“重新转录”的。它只是完全相同的东西。 - Andreas Rejbrand
@Andreas:是的,你说得对。我解释得不好。 - Beny
有关编程的内容:何时以及如何混淆Delphi代码? - Warren P
2个回答

23

一个非常简单的方法是将字符串通过ROT13方法进行混淆并存储。

procedure ROT13(var Str: string);
const
  OrdBigA = Ord('A');
  OrdBigZ = Ord('Z');
  OrdSmlA = Ord('a');
  OrdSmlZ = Ord('z');
var
  i, o: integer;
begin
  for i := 1 to length(Str) do
  begin
    o := Ord(Str[i]);
    if InRange(o, OrdBigA, OrdBigZ) then
      Str[i] := Chr(OrdBigA + (o - OrdBigA + 13) mod 26)
    else if InRange(o, OrdSmlA, OrdSmlZ) then
      Str[i] := Chr(OrdSmlA + (o - OrdSmlA + 13) mod 26);
  end;
end;

function ROT13fun(const Str: string): string;
begin
  result := Str;
  ROT13(result);
end;

const
  ObfuscatedString = 'Guvf vf n frperg zrffntr.';

procedure TForm4.FormCreate(Sender: TObject);
begin
  ShowMessage(ROT13fun(ObfuscatedString));
end;
稍微高级一些的方法是使用凯撒密码或维吉尼亚密码来进行操作。
要获得用于源代码中的混淆字符串,您可以使用像我的Rejbrand文本编辑器Wolfram|Alpha这样的良好文本编辑器。
更新
ROT13 很容易被解密,但根据情况也许已经足够了!至少在二进制文件中查找字符串会变得非常困难。需要付出真正的努力才能获得这些字符串。毕竟,在十六进制编辑器/文本编辑器中,普通用户甚至不会看到二进制文件! 凯撒密码是 ROT13 密码的一个非常简单的推广,并且也很容易被解密。实际上,只有 25 种不同的“密码”。 维吉尼亚密码则更加棘手,并需要一些真正的努力去破译(特别是因为您无法确切知道字符串在二进制文件中的位置)。
作为例子,下面是一个使用维吉尼亚密码混淆的字符串:
  

Xlc tsrgcdk sj ‘vrivem’ mw cei sd kli acirivqhfriw cw qsbsir tfmjmgw, rrh biimrk hyi pygk gilhlvc mf ws, wk leq rpws pvgsqc fj agrvwtvcou mrrsiiwx we izcfp-hew cmji, rpxlmixl ml r piqg xigfbzgep zrrkyyuv. Mlrvih, hyi qmrvvr qctmixw vbtpmwkw ilsikc qclvgiq ks wsqy er soxirr klex hyi ilhzvi cbmmvslavrx mt xli Srvxl wj irboekivcr. Mr hymw qstxmsl, ai uwcp mljvwxmeoki xfs tlcqwtep zojmw mt xli seivkw tsrgcdk.

当然,可以将密码扩展到数字和特殊字符,包括空格。 它还可以混合大写字母和小写字母。那么它将非常难以破解(尽管有可能)。 如果密码是已知单词,则破译会更容易,在字典中可以找到该单词。 如果不是单词,则更安全。
上面的文本使用一个可以在足够大的字典中找到的单词进行混淆。 下面的文本使用无意义的字符串作为密码进行混淆:
  

Miwzvjfy m vjsy-tombox zguol ap ahqovz d uwk sbze w conz pe biusvth pagh h njsx. Io puvyeq, fl cjsx xic vmovdq zappzjvz, vnjnatl frcb vy dtmd vhxkt fto babtf davf. Uuxlhqb, khk aa dbn eumsuzq, auk saed vlpnbuuo ywlemz ue pnyl ttmxv. Pa ghof, fl cjsx kmbbzk atmd wv sfjtmxcl rtfysk cb yuta md jsy. Sqf nql njsx ly vs ilusrn o gok uxwupagupaz u.

最后,下面的文本使用相同的方式进行混淆,但是还从字符串中删除了所有空格和特殊字符:
  

cishkclruervutzgnyarkgzjsaqgsrzvmmrzweolpcnvbkxrvdnqrlurhpmhfaxsuoqncxgz

const
  OrdBigA = Ord('A');
  OrdBigZ = Ord('Z');
  OrdSmlA = Ord('a');
  OrdSmlZ = Ord('z');

function imod(const x: integer; const y: integer): integer;
begin
  if x >= 0 then
    imod := x - floor(x/y) * y
  else
    imod := x + ceil(-x/y) * y;
end;

procedure Vigenère(var Str: string; const Key: string);
var
  n, i, o: integer;
  KeyChrs: TBytes;
begin

  n := length(Key);
  SetLength(KeyChrs, n);
  for i := 1 to n do
    if InRange(ord(Key[i]), OrdBigA, OrdBigZ) then
      KeyChrs[i - 1] := Ord(Key[i]) - OrdBigA
    else
      raise Exception.Create('Invalid character in Vigenère key.');

  for i := 1 to length(Str) do
  begin
    o := Ord(Str[i]);
    if InRange(o, OrdBigA, OrdBigZ) then
      Str[i] := Chr(OrdBigA + imod((o - OrdBigA + KeyChrs[(i-1) mod n]), 26))
    else if InRange(o, OrdSmlA, OrdSmlZ) then
      Str[i] := Chr(OrdSmlA + imod((o - OrdSmlA + KeyChrs[(i-1) mod n]), 26));
  end;

end;

function Vigenèref(const Str: string; const Key: string): string;
begin
  result := Str;
  Vigenère(result, Key);
end;

procedure VigenèreD(var Str: string; const Key: string);
var
  n, i, o: integer;
  KeyChrs: TBytes;
begin

  n := length(Key);
  SetLength(KeyChrs, n);
  for i := 1 to n do
    if InRange(ord(Key[i]), OrdBigA, OrdBigZ) then
      KeyChrs[i - 1] := Ord(Key[i]) - OrdBigA
    else
      raise Exception.Create('Invalid character in Vigenère key.');

  for i := 1 to length(Str) do
  begin
    o := Ord(Str[i]);
    if InRange(o, OrdBigA, OrdBigZ) then
      Str[i] := Chr(OrdBigA + imod((o - OrdBigA - KeyChrs[(i-1) mod n]), 26))
    else if InRange(o, OrdSmlA, OrdSmlZ) then
      Str[i] := Chr(OrdSmlA + imod((o - OrdSmlA - KeyChrs[(i-1) mod n]), 26));
  end;

end;

function VigenèreDf(const Str: string; const Key: string): string;
begin
  result := Str;
  VigenèreD(result, Key);
end;

1
它完美地运行!非常感谢。目前我会继续使用 ROT13,但我会了解 Caesar。而且你的文本编辑器会很有帮助 :) - Beny
4
为给一个Delphi函数命名加上法语è重音符号点赞。请在Delphi 7中尝试这个功能名称。 :-) - Warren P
2
@Warren P:那你还没见过我的Möbius函数呢! - Andreas Rejbrand
@AndreasRejbrand,你在PHP中有这些函数吗?我非常感兴趣使用这种方法在Delphi应用程序和PHP Web服务之间进行远程通信,发送和接收加密指令。 - NaN
@EASI:不,我没有,但翻译起来应该不难。但你知道这并不是真正的“加密”。 - Andreas Rejbrand
显示剩余5条评论

0

你可以使用像this question中提到的真正的加密库。一个小型的外部工具可以将您的原始字符串转换为静态字节数组,该数组将被编译到您的应用程序中,并在内存中解密和还原为字符串。这样做的额外好处是它不会看起来像ASCII(范围32..127),并且对于使用十六进制编辑器的普通检查人员来说不会那么明显。

此外,真正的加密(即使是3DES或Blowfish)也不会像ROT13或单层凯撒密码那样容易被移除,而无需查看您的源代码或反向工程您的二进制文件。但是,如果您用于解密/加密的密钥本身没有受到保护,那么它并不像您所希望的那样安全。对我来说,甚至可以将调试器附加到您的代码(即使是发布版本),在运行时恢复和记录字符串堆中的所有字符串,而无需费心破解您的加密,即使您使用上述真正的库。我同意Andreas的观点,Vigenere似乎是您的目的所需的一种合理的保护和努力,而我认为ROT13和单层凯撒密码本身有点可笑。更新:Andreas发布的Vigenere算法非常出色,我更喜欢它而不是一个庞大的外部库,在您的情况下。
对于您的特定情况,即存储文本文件中的某些内容,我会编写一个小型实用程序,将您的秘密加密和编码到源代码中,并将密钥存储在至少其他地方。这就像将弹药放在保险箱中,并将钥匙放在其他地方。尽管这也不是完美的,但比ROT13更好,后者是所有玩具加密风格中最“玩具”式的一种。

维吉尼亚密码比凯撒密码(ROT13是凯撒密码的特例)更难破解。(而ROT13也是所讨论的函数的唯一自反函数。) - Andreas Rejbrand
有Vigenère的代码示例吗?应该只是凯撒技术的组合吧?这是一个很酷的历史参考。1553年的算法!如果生成的文本仍然可以读取ASCII,则在查看生成的目标可执行文件时会非常突出。 - Warren P
哇,这是一个史诗级的黑客攻击!好样的,安德烈亚斯! - Warren P
感谢Warren提供的想法。此外,我不知道加密库,我会去了解一下。 - Beny

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接