从 Inno Setup [代码] 中反汇编字符串

Disassembling strings from Inno Setup [Code]

当我编译 Inno Setup 项目时,[Code] 部分也被编译(作为 Pascal 可执行文件或 Pascal DLL)?

换句话说,如果有人解压一个 Inno Setup 项目,他能看到 [Code] 部分是原始源代码(该死的!:))还是已编译的 executable/DLL(难以反汇编) ?

我想在 [Code] 部分中插入一些字符串(密码和密钥),但我不知道在逆向工程方面知之甚少的情况下是否也可以轻松恢复它们。

代码被编译成某种二进制表示形式(非常粗略地类似于 .NET CIL or Java bytecode)。

Inno Setup Unpacker(和其他),可以从 Inno Setup 生成的 .exe 中提取文件。它可以将代码的二进制表示提取到 CompiledCode.bin(如果您使用 -x -m 标志)。

然后您可以使用能够将 decompile/disassemble CompiledCode.bin 文件转换为(伪)Pascal 脚本代码的 Inno Setup Decompiler 项目。但与 .NET 或 Java 的逆向工程一样,它不会为您提供准确的代码。反编译后的代码可能甚至无法编译(至少我上次尝试时是这样),但足以看出代码的作用。他们现在好像有付费版,可能比我前段时间试过的免费版要好。 (最新版本的Inno Setup Decompiler甚至可以直接从.exe中提取代码,但还没有更新到最新版本的Inno Setup [5.6.1],所以它不适用于我。)

(Inno Setup Decompiler 站点消失了,但它并没有改变任何关于 decompile/disassemble 编译代码在技术上是可能的事实)


很容易看到代码中编译的文字字符串,即使在 CompiledCode.bin.

中也是如此

例如这些凭据:

Username := 'secretusername';
Password := 'mysupersecretpassword';

CompiledCode.bin文件中可以这样看:

当然,您可以 obfuscate 字符串(至少 hex-encode 它们)。但正如您所希望知道的那样,无论您做什么,一旦(甚至编译过的)代码出现在用户的机器上,您就没有办法绝对保护它。

存储字符串文字的简单支持代码hex-encoded:

function CryptStringToBinary(
  sz: string; cch: LongWord; flags: LongWord; binary: AnsiString; 
  var size: LongWord; skip: LongWord; flagsused: LongWord): Integer;
  external 'CryptStringToBinaryW@crypt32.dll stdcall';

const
  CRYPT_STRING_HEX = ;

function UnobfuscateString(S: string): string;
var
  Size: LongWord;
  Buffer: AnsiString;
  Len: Integer;
begin
  SetLength(Buffer, (Length(S) div 2) + 1);
  Len := Length(S);
  Size := Len div 2;
  if (CryptStringToBinary(S, Len, CRYPT_STRING_HEX, Buffer, Size, 0, 0) = 0) or
     (Size <> Length(S) div 2) then
  begin
    RaiseException('Error unobfuscating string');
  end;
  Result := Buffer;
end;

#define ObfuscateString(str S) \
  Local[0] = AddBackslash(GetEnv("TEMP")) + "ObfuscatedString.pas", \
  Local[1] = \
    "-ExecutionPolicy Bypass -Command """ + \
    "$bytes = [Text.Encoding]::ASCII.GetBytes('" + S + "'); " + \
    "$s = '''' + (($bytes | foreach { $_.ToString('X2') }) -join '') + ''''; " + \
    "Set-Content -Path '" + Local[0] + "' -Value $s;" + \
    """", \
  Exec("powershell.exe", Local[1], SourcePath, , SW_HIDE), \
  Local[2] = FileOpen(Local[0]), \
  Local[3] = FileRead(Local[2]), \
  FileClose(Local[2]), \
  DeleteFileNow(Local[0]), \
  "UnobfuscateString(" + Local[3] + ")"

(该代码已在 上测试。不过,它只能使用 ASCII 密码。)

借助上面的代码,您可以这样写(这样您就可以在源代码中轻松编辑凭据):

Username := {#ObfuscateString("secretusername")};
Password := {#ObfuscateString("mysupersecretpassword")};

但代码将被编译为:

Username := UnobfuscateString('736563726574757365726E616D65');
Password := UnobfuscateString('6D79737570657273656372657470617373776F7264');

您可以通过将此添加到 .iss 脚本的末尾并检查生成的 Preprocessed.iss 文件来验证这一点。

#expr SaveToFile(AddBackslash(SourcePath) + "Preprocessed.iss")

因此,尽管凭据在源代码中是可读的,但它们不会按字面意义存储到编译代码中:

但同样,这只是一种混淆。任何具有良好编程技能的人都能够取消混淆(解密)凭据。