反编译与反汇编

时间:2021-09-26 01:06:41

反编译做的就是编译的相反动作。举例来说,例如一个程序用C语言来写,写完之后要编译成CPU可以识别和运行的机器代码,也就是生成EXE程序。而反编译是反过来,把EXE反编译成编程源代码,也就是C程序,不过一般反编译大多数都是从机器码反编译为汇编。其他语言的情况基本也是这样的。至于具体要怎么用,这个就看你的目的了。但一般情况下是通过反编译主要是用于没有源码的程序的分析,例如破解别人的软件等。

一,比如说,你拿到人家的一个程序,是一个EXE文件,经过用工具分析,知道人家是用delphi写的,于是你用一个反编译工具把这个程序从EXE反编译为delphi源代码,这样你就可以对源代码进行修改和完善,然后再重新编译成EXE。
二,再比如你拿到一个程序,这程序需要注册,不注册不能用或者只能试用几次,注册后就能一直使用,因此你想对它进行破解。但EXE是CPU能识别的代码,在我们看起来是所谓的“乱码”,于是要找反编译工具,把它反编译成我们能看得懂的汇编语言,这样就能看到程序是怎么判断是否注册的,然后再根据找到的判断位置对其进行修改,就能做出破解版了。

对于上面这两种反编译的情况,不懂编程的人,第一种情况反编译出来的源代码,肯定看不懂,更别说对其进行完善了,根本就没意义,至于第二种情况,不但要有很深的编程水平还要非常有经验的人才能做到,反编译出来的汇编源码动辄上万行甚至几十万行,要从这么多行里面找出判断点,不是说学过一点编程的人就能做到的,更别说没学过编程的了。
  
  
 
 

 要更深入了解C++, 必须要知道一个程序从开始到结束都干了些什么, 怎么干的。 所以我从C++编译到运行过程,解析下程序是怎么跑的。

        首先,初略的说一下之前C++的编译过程,C++编译过程包括预编译-》汇编-》编译-》链接。称为一个可执行文件。(Windows平台下为.exe文件)。

预编译主要展开包含的头文件,宏定义等操作。例如一个简单的main程序,编译预编译后,的文件对比。

反编译与反汇编反编译与反汇编  可以看到里面的宏已经被去掉了。如果定了那个宏,那么宏里面的内容也会显示出来。头文件也是,如果你包含了你一个.h 文件,那么整个.h文件会包含进来。

     汇编过程,就是把已经预编译的文件编译成汇编代码的过程,整个过程会包含语法,词法的分析,和一些优化操作。

     编译过程其实是跟汇编可以合成一个阶段,变成目标代码。也就是二进制文件。

     链接过程是将单个编译后的文件链接成一个可执行程序。前面的预编译、汇编、编译都是正对单个文件,以一个文件为一个编译单元,而链接则是将所有关联到的编译后单元文件和应用的到库文件,进行一次链接处理,之前编译过的文件 如果有用到其他文件里面定义到的函数,全局变量,在这个过程中都会进行解析。

首先看看编译后的文件样子(已VS2012编译后的OBJ文件为例子,不同编译器 样式可能会不同。)

编译前的文件

#include "Car.h"
int main(int argc, char* argv[])
{
 Car* p = new Car();

 delete p;
 return 1;
}


编译后的样子(由于编译后的文件 信息太多 只贴出里面未解析符号部分。)

 UNDEF:00002DC4 ; int __thiscall Car::Car(Car *__hidden this)
UNDEF:00002DC4                 extrn ??0Car@@QAE@XZ:near ; CODE XREF: _main+63p
UNDEF:00002DC8 ; int __thiscall Car::~Car(Car *__hidden this)
UNDEF:00002DC8                 extrn ??1Car@@QAE@XZ:near
UNDEF:00002DC8                                         ; CODE XREF: Car::`scalar deleting destructor'(uint)+26p
UNDEF:00002DCC ; __fastcall _RTC_CheckStackVars(x, x)
UNDEF:00002DCC                 extrn @_RTC_CheckStackVars@8:near
UNDEF:00002DCC                                         ; CODE XREF: std::_String_alloc<0,std::_String_base_types<char,std::allocator<char>>>::_Alloc_proxy(void)+68p
UNDEF:00002DCC                                         ; $LN19+72p ...
UNDEF:00002DD0 ; __fastcall __security_check_cookie(x)
UNDEF:00002DD0                 extrn @__security_check_cookie@4:near
UNDEF:00002DD0                                         ; CODE XREF: __ehhandler$??$construct@PADAAPAD@?$allocator@D@std@@QAEXPAPADAAPAD@Z+Fp
UNDEF:00002DD0                                         ; __ehhandler$??$construct@U_Container_proxy@std@@U12@@?$allocator@U_Container_proxy@std@@@std@@QAEXPAU_Container_proxy@1@$$QAU21@@Z+Fp ...
UNDEF:00002DD4 ; __stdcall _CxxThrowException(x, x)

编译后的文件用(用反汇编成汇编代码查看) 其中实现函数会变成一堆汇编指令。而那些引用到的在其他文件里面实现的函数将会变成一个特点的符号(如上面中的调用Car类的构造函数 extrn ??0Car@@QAE@XZ:near)这些符号称做为解析的符号,表示在链接的时候需要被解析。符号的生成名称具体跟编译器有关,但是会保证一个类的某个函数名称在同一个编译里面必须是唯一的,因为我们在预编译阶段已经把Car.h包含进来所以编译器能正确生成这个函数的名字,然后在链接的时候 会找到改名字的函数,把此标识名字替换为函数的地址。这样就实现的链接。

在符号解析(symbol resolution)阶段,链接器按照所有目标文件和库文件出现在命令行中的顺序从左至右依次扫描它们,在此期间它要维护若干个集合:(1)集合E是将被合并到一起组成可执行文件的所有目标文件集合;(2)集合U是未解析符号(unresolved symbols,比如已经被引用但是还未被定义的符号)的集合;(3)集合D是所有之前已被加入到E的目标文件定义的符号集合。一开始,E、U、D都是空的。

(1): 对命令行中的每一个输入文件f,链接器确定它是目标文件还是库文件,如果它是目标文件,就把f加入到E,并把f中未解析的符号和已定义的符号分别加入到U、D集合中,然后处理下一个输入文件。

(2): 如果f是一个库文件,链接器会尝试把U中的所有未解析符号与f中各目标模块定义的符号进行匹配。如果某个目标模块m定义了一个U中的未解析符号,那么就把 m加入到E中,并把m中未解析的符号和已定义的符号分别加入到U、D集合中。不断地对f中的所有目标模块重复这个过程直至到达一个不动点(fixed point),此时U和D不再变化。而那些未加入到E中的f里的目标模块就被简单地丢弃,链接器继续处理下一输入文件。

(3): 如果处理过程中往D加入一个已存在的符号,或者当扫描完所有输入文件时U非空,链接器报错并停止动作。否则,它把E中的所有目标文件合并在一起生成可执行文件。


反编译与反汇编

原文地址http://blog.csdn.net/yinzhuo1/article/details/47069201




C/C++程序编译流程(预处理->编译->汇编->链接)

程序的基本流程如图:

1. 预处理

预处理相当于根据预处理指令组装新的C/C++程序。经过预处理,会产生一个没有宏定义,没有条件编译指令,没有特殊符号的输出文件,这个文件的含义同原本的文件无异,只是内容上有所不同。

  • 读取C/C++源程序,对其中的伪指令(以#开头的指令)进行处理

    ①将所有的“#define”删除,并且展开所有的宏定义

    ②处理所有的条件编译指令,如:“#if”、“#ifdef”、“#elif”、“#else”、“endif”等。这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉。 

    ③处理“#include”预编译指令,将被包含的文件插入到该预编译指令的位置。

(注意:这个过程可能是递归进行的,也就是说被包含的文件可能还包含其他文件)

  • 删除所有的注释

  • 添加行号和文件名标识。

    以便于编译时编译器产生调试用的行号信息及用于编译时产生的编译错误或警告时能够显示行号

  • 保留所有的#pragma编译器指令

2. 编译

将预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后,产生相应的汇编代码文件。

3. 汇编

将编译完的汇编代码文件翻译成机器指令,并生成可重定位目标程序的.o文件,该文件为二进制文件,字节编码是机器指令。

汇编器是将汇编代码转变成机器可以执行的指令,每一个汇编语句几乎都对应一条机器指令。所以汇编器的汇编过程相对于编译器来讲比较简单,它没有复杂的语法,也没有语义,也不需要做指令优化,只是根据汇编指令和机器指令的对照表一一翻译即可。

4. 链接

通过链接器将一个个目标文件(或许还会有库文件)链接在一起生成一个完整的可执行程序。

    由汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。

例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经链接程序的处理方能得以解决。

    链接程序的主要工作就是将有关的目标文件彼此相连接,也就是将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。


原文地址:http://www.cnblogs.com/Lynn-Zhang/p/5377024.html


C++ 之编译过程

 

1)用户点击编译程序时,编译程序将C++源代码转换成目标代码,目标代码通常由 机器指令和记录如何将程序加载到内存的信息组成。其后缀通常为.obj或.o;

2)目标文件中存储的只是用户所编写的代码的转换结果,并不包括底层的操作指令,不能直接运行。例如程序包 iostream 实现了所有有关输入和输出的操作,并且其所有实现操作的机器代码都放在一个库中,库是对已实现的程序经编译后所产生的代码集合,用户可以在程序中直接使用库。

3)一个被称为链接程序的特殊程序将用户程序的目标文件和iostream库中必要代码链接起来生成一个可执行文件,其后缀通常为.exe 。这个可执行文件中包含了执行该用户程序所需要的所有机器代码,其过程大体如下所示:

 反编译与反汇编

原文地址:http://www.cnblogs.com/debuging/archive/2011/10/16/2214195.html