直接修改二进制可执行文件而不破坏其可行性

时间:2018-09-11 10:28:00

标签: python c security

首先,以防万一有人想知道,这个问题仅出于教育的目的。

说我已经得到了一个二进制可执行文件(从C代码编译而成)。该二进制可执行文件带有一个参数,一个密码,如果密码正确,它将写出一条秘密消息。实际上,$./jeff-binary jeffspassword会产生Secret: Jeff's Secret Message

我希望在绕过知道密码(jeffspassword的必要性的同时知道这个秘密。我知道在创建这些二进制文件的C代码中,包含密码的路径是硬编码的。代码中将包含以下内容:fp = fopen("/etc/secret-password-dir/jeff/password", "r");显然,由于我不是Jeff,所以我没有读取或写入jeff的权限。

由于我可以访问此二进制文件,因此当前执行的操作将以单个长十六进制字符串形式在二进制文件中读取,然后在其中搜索与十六进制字符串匹配的字符串(根据示例)/etc/secret-password-dir/jeff/password并将其替换为/home/fred/Documents/blank_password,然后创建一个具有此更改的新二进制文件。目的是使文件指针最终假定密码是我输入的blank_password(我知道)的密码,因此,我可以使用非密码运行此修改过的二进制文件,以便打印出Jeff的秘密。 / p>

具体来说,我当前正在运行的进程是这样​​的:

  1. 我有一个C程序,该程序读取jeff-binary的内容并将其作为十六进制字符串写入hex_of_jeff_binary.txt

  2. 然后我有了一个Python脚本,该脚本读取hex_of_jeff_binary.txt通过用/etc/secret-password-dir/jeff/password替换/home/fred/Documents/blank_password的十六进制表示来进行一些字符串理解,然后将此修改后的二进制写为{{1 }}。

  3. jeff-binary-mod使其可执行。

  4. 结果是:chmod +x jeff-binary-mod。嗯。

我的问题是:我试图做的可能吗?如果是这样,我哪里出问题了?

1 个答案:

答案 0 :(得分:0)

据我所知,我试图重现OP打算实现的目标。

  1. 我用C语言编写了一个小密码“安全”应用程序。

testSecret.c

#include <string.h>
#include <stdio.h>

int main(int argc, char **argv)
{
  const char *password = "MySecretPassword";
  if (argc != 2) {
    fprintf(stderr, "ERROR! Wrong number of command line arguments.\n");
    return -1;
  }
  if (strcmp(argv[1], password) == 0) {
    printf("Hello, proper receiver of secret message.\n");
  } else {
    fprintf(stderr, "Nice try but FAILED!\n");
  }
  return 0;
}

已在Windows 10的cygwin64中进行了编译和测试:

$ gcc -std=c11 -o testSecret testSecret.c

$ ./testSecret 
ERROR! Wrong number of command line arguments.

$ ./testSecret wrong
Nice try but FAILED!

$ ./testSecret MySecretPassword
Hello, proper receiver of secret message.

$
  1. 我使用Hex-Editor插件将二进制文件加载到了Notepad++中。我正在寻找password的初始化文本。

snapshot of Notepad++ with found string constant

  1. 常量字符串的第一个字节被0覆盖。(重要的是不要插入或擦除任何字节。否则,后面的所有地址都将变为错误,并且二进制文件肯定会损坏。)“ patched”文件另存为testSecretCracked.exe

snapshot of Notepad++ after patching and saving the binary

  1. 测试破解的二进制文件:
$ ./testSecretCracked.exe 
ERROR! Wrong number of command line arguments.

$ ./testSecretCracked.exe ""
Hello, proper receiver of secret message.

$

破解后的新密码现在为""。因此,秘密就减少了在bash上传递带有空字符串的参数的经验。


当然,这仅是出于娱乐/教育目的的演示。导致失败的原因有很多(例如,评论中提到的原因)。商业应用程序可能包含加密数据(在运行时解密)。一个简单的检查可能是对内部数据进行哈希处理,然后将其与哈希代码进行比较,该哈希代码会取消屏蔽大部分修改。 (顺便说一句,我们的商业应用程序附带了H / W许可证检查,该检查将上述安全技术与其他安全技术结合在一起。)


只是为了好玩,我如上所述自动执行了简单的“破解”操作:

testAutoCrack.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)
{
  if (argc != 4) {
    fprintf(stderr, "ERROR! Wrong number of command line arguments.\n");
    return -1;
  }
  const char *inFile = argv[1];
  const char *outFile = argv[2];
  const char *password = argv[3];
  /* read binary */
  size_t size = 0x1000;
  char *buffer = malloc(size);
  if (!buffer) {
    fprintf(stderr, "ERROR! Out of memory.\n");
    return -1;
  }
  FILE *fIn = fopen(inFile, "rb");
  if (!fIn) {
    fprintf(stderr, "ERROR! Failed to open '%s'.\n", inFile);
    return -1;
  }
  size_t lenTotal = 0;
  for (;;) {
    size_t lenRead = size - lenTotal;
    size_t len = fread(buffer + lenTotal, 1, lenRead, fIn);
    lenTotal += len;
    if (len < lenRead) break; // EOF
    char *bufferNew = realloc(buffer, 2 * size);
    if (!bufferNew) {
      fprintf(stderr, "ERROR! Out of memory.\n");
      return -1;
    }
    buffer = bufferNew;
    size *= 2;
  }
  fclose(fIn);
  /* find password in binary */
  size_t lenPassword = strlen(password) + 1;
  if (lenTotal < lenPassword) {
    fprintf(stderr, "ERROR! Password longer than binary.\n");
    return -1;
  }
  size_t i = lenTotal - lenPassword;
  while (i-- && strncmp(buffer + i, password, lenPassword) != 0);
  if (i >= lenTotal) {
    fprintf(stderr, "Password '%s' not found!\n", password);
    return -1;
  }
  /* patch password */
  buffer[i] = '\0';
  /* write binary */
  FILE *fOut = fopen(outFile, "wb");
  if (!fOut) {
    fprintf(stderr, "ERROR! Failed to open '%s'.\n", outFile);
    return -1;
  }
  if (fwrite(buffer, 1, lenTotal, fOut) < lenTotal
    || fclose(fOut)) {
    fprintf(stderr, "ERROR! Failed to write '%s'.\n", outFile);
    return -1;
  }
  /* done */
  printf("'%s' successfully cracked.\n", inFile);
  return 0;
}

经过编译和测试:

$ gcc -std=c11 -o testAutoCrack testAutoCrack.c 

$ ./testAutoCrack testSecret.exe testSecretAutoCracked.exe MySecretPassword
'testSecret.exe' successfully cracked.

$ chmod a+x testSecretAutoCracked.exe

$ ./testSecretAutoCracked.exe ""
Hello, proper receiver of secret message.

$