我需要从可执行文件中恢复我的源代码

时间:2011-09-11 16:19:02

标签: c disassembly

这是半夜,我通过输入

意外地覆盖了我的所有工作
gcc source.c -o source.c

我仍然拥有原始的二进制文件,我唯一的希望就是解读它,但我不知道如何或最好的工具来获得最可读的结果。我知道这可能不是发布的正确位置,但我很强调。有人可以帮帮我吗?

6 个答案:

答案 0 :(得分:4)

有几种工具(您可以使用Google进行搜索),但我建议您重新编码。您将投资重构反汇编程序将重新编码的时间可能高于重新编码。

我知道这似乎很明显,但正确的答案是:从备份恢复(你应该拥有)

答案 1 :(得分:4)

感谢您上传文件。我怀疑它是未被剥离的,所以功能名称仍然存在。除了标准的样板代码,我还可以识别函数mainregister_brokerconnect_exchange(未使用和空)和handle_requests

我在IDA Pro上花了一些时间,恢复main()功能并不太难。首先,这是来自IDA的main()的原始未经修改的列表:http://pastebin.com/sBxhRJMM

要继续,您需要熟悉AMD64 calling convention。总而言之,前四个参数在RDI(EDI),RSI(ESI),RDX(EDX)和RCX(ECX)中传递。其余的都在堆栈上传递,但main()中的所有调用只使用最多四个参数,所以我们不需要担心。

IDA帮助标记了标准C函数的参数,甚至重命名了一些局部变量。但是,它可以进一步改进和评论。例如,由于我们在main(),我们知道argc(第一个参数)来自EDI(因为它是int意味着32位,它只使用低半部分RDI)和argv来自RSI(它是一个指针,因此它使用寄存器的完整8个字节)。因此,我们可以重命名复制EDI和RSI的局部变量:

mov     [rbp+argc], edi
mov     [rbp+argv], rsi

接下来是一个简单的条件块:

cmp     [rbp+argc], 2
jz      short loc_400EB3            
mov     rax, cs:stderr@@GLIBC_2_2_5 
mov     rdx, rax                    
mov     eax, offset aUsage ; "Usage"
mov     rcx, rdx        ; s         
mov     edx, 5          ; n         
mov     esi, 1          ; size      
mov     rdi, rax        ; ptr       
call    _fwrite                     
mov     edi, 1          ; status    
call    _exit                       

这里我们将argc与2进行比较,如果它相等,我们会在代码中进一步跳转。如果相等,我们会调用fwrite()。它的第一个参数是rdirdi是从rax加载的,esi包含一个常量字符串“Usage”的地址。第二个参数位于edx并且为1,是rcx中的第三个参数,并且是rdx中的第四个参数,它是从stderr@@GLIBC_2_2_5加载的,其值为{ {1}},它基本上是对来自libc的stderr变量的奇特引用。将它们串起来,我们得到:

fwrite("Usage", 1, 5, stderr);

根据我的经验,我可以说很可能是内联fprintf,因为5正好是字符串的长度。即原始代码可能是:

fprintf(stderr, "Usage");

下一个电话是一个简单的exit(1);。结合两者进行比较,我们得到:

if ( argc != 2 )
{
  fprintf(stderr, "Usage");
  exit(1);
}

继续这样,我们可以识别他们使用的其他调用和变量。描述这一切有点单调乏味,所以我上传了反汇编的评论版本,在那里我尝试显示每个调用的等效C代码。你可以在这里看到它:http://pastebin.com/p5sRSwgQ

从评论的版本来看,想象main()的可能版本并不是很难:

int main(int argc, char **argv)
{
  if ( argc != 2 )
  {
    fprintf(stderr, "Usage");
    exit(1);
  }
  char name[256];
  gethostname(name, sizeof(name));
  struct hostent* _hostent = gethostbyname(name);
  struct in_addr *_addr0 = (struct in_addr *)(_hostent->h_addr_list[0]);
  struct sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_port = htons(0);
  addr.sin_addr.s_addr = _addr0->s_addr;
  char *tmp = (char *)malloc(6);
  sprintf(tmp, "%d", addr.sin_port);
  char *ip_str = inet_ntoa(*_addr0);
  char *newbuf = (char *)malloc(strlen(argv[1]) + strlen(ip_str) + strlen(tmp) + 5);
  strcpy(newbuf, "r");
  strcat(newbuf, " ");
  strcat(newbuf, argv[1]);
  strcat(newbuf, " ");
  strcat(newbuf, ip_str);
  strcat(newbuf, " ");
  strcat(newbuf, tmp);
  register_broker(newbuf);
  int fd = socket(PF_INET, SOCK_STREAM, 0);
  if ( fd < 0 )
  {
    perror("Error creating socket");
    exit(1);
  }
  if ( bind(fd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )
  {
    perror("Error binding socket");
    exit(1);
  }
  if ( listen(fd, 0x80) != 0 )
  {
    perror("Error listening on socket");
    exit(1);
  }
  handle_requests(fd);
}

恢复其他两个功能是为读者留下的练习:)

答案 2 :(得分:3)

遗憾的是,从二进制文件回到源代码确实没有好办法。你可以尝试Boomerang,但我真的不希望得到好的结果。

答案 3 :(得分:2)

首先,查找备份源文件。大多数编辑器会为每个文件保存创建名为.bakfilename.c~的文件。在Windows计算机上,取证软件工具可能能够检索最后的源文件。我写的工具getfile曾经由NTI提供,但几年前被Armor Holdings收购 - 不知道它是否仍然可用。

如果代码是可运行的,通常在strace()实用程序(Linux发行版的标准组件)下运行它可以帮助解码程序的某些方面,特别是如果它是面向i / o的。唉,如果该程序主要是内部数据操作,那么这没什么用处。 Strace()创建程序传递的系统调用和参数的日志;它有时是理解程序行为的宝贵工具。例如,strace date生成(部分 - 我省略了运行时库启动):

clock_gettime(CLOCK_REALTIME, {1315760058, 681379835}) = 0
open("/etc/localtime", O_RDONLY)        = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=2819, ...}) = 0
fstat64(3, {st_mode=S_IFREG|0644, st_size=2819, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb78b5000
read(3, "TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\4\0\0\0\0"..., 4096) = 2819
_llseek(3, -24, [2795], SEEK_CUR)       = 0
read(3, "\nPST8PDT,M3.2.0,M11.1.0\n", 4096) = 24
_llseek(3, 2818, [2818], SEEK_SET)      = 0
close(3)                                = 0
munmap(0xb78b5000, 4096)                = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb78b5000
write(1, "Sun Sep 11 09:54:18 PDT 2011\n", 29Sun Sep 11 09:54:18 PDT 2011) = 29
close(1)                                = 0
munmap(0xb78b5000, 4096)                = 0
close(2)                                = 0

只要你有值得保存的东西:

  • 添加某种源代码控制(git,svn,cvs,...)可能不止一个
  • 使用自动构建工具,例如make,以避免愚蠢的错误
  • 偶尔进行备份。即使我在一个石刀和熊皮客户端,我仍然可以通过电子邮件发送自己的源文件,以获得最后的备份机制。

答案 4 :(得分:1)

您可以使用dcc。但是,下次你应该使用Git;)

答案 5 :(得分:0)

您可以尝试使用objdump -d <filename>进行反汇编。

您还可以使用nm实用程序查看符号名称,以慢慢记忆并帮助重新编码来源。

商业IDA Pro反汇编程序/调试程序在软件逆向工程中很受欢迎。不幸的是,对二进制文件进行逆向工程是一项缓慢而困难的工作。