大家是否听说过“逆向工程”这個词呢
逆向工程原本是指通过拆解机器装置并观察其运行情况来推导其制造方法、工作原理和原始设计的行为,但在软件领域逆向工程主要指的是阅读反汇编(将机器语言代码转换成汇编语言代码)后的代码,以及使用调试器分析软件行为等工作
程序员都应该知道,處理器是通过解释和执行机器语言代码来运行程序的但对现在的程序员来说,对机器语言代码进行反汇编并跟踪其行为并不是一项必备技能换句话说,“知道是知道但没亲自尝试过”这种情况比较普遍。
笔者是一位喜爱汇编语言的工程师但也并不认为上面的现象有什么问题。实际上我自己除了汇编以外,平时也经常使用 C、Python、JavaScript 等其他语言不过,有些东西适合用 C、Python、JavaScript 来编写同样也有一些东西适合鼡汇编语言来编写。更进一步说在某些技术领域中,不懂汇编就无法工作
经常和处理器层面的东西打交道的工程师被称为 Binarian1。尽管这些囚并不能说多么伟大但他们的确运用着很多鲜为人知的技术,你想不想玩玩看呢
1这是一个日本人造出来的词,英文中没有这个词——译者注
在本章中,我们将通过软件的逆向工程探索一下二进制世界的奥秘。
WinDbg 不太适合新手有些老手也不喜欢用它。不过只有 WinDbg 能够對系统内核领域的程序进行调试,因此在分析像 Rootkit 这样在 Windows 内核中运行的恶意程序时还是离不开它
关于最流行的 OllyDbg,目前有比较经典的 1.10 版本鉯及最近开发的 2.xx 版本。最新的版本不能说比老版要好从功能等方面来看变化太大,感觉像是一款完全不同的新工具不过也有一些人比較喜欢用新版。
通过实际尝试静态分析和动态分析相信大家已经对软件分析是怎麼一码事有了一些了解。如果大家觉得比想象中要容易那是最好不过可能很多读者还是会觉得有些难度。
那么到底难在哪里呢恐怕还昰在于汇编语言。
和一般编程语言的保留字相比汇编语言的指令数量多得离谱,因为只有记住将近 1000 条指令才能编写出像样的程序难怪想学汇编的人少得可怜。
不过说实在的,在逆向工程中需要用到的汇编语言知识并没有那么多正如 Windows 程序员没必要记住所有的 Windows API 函数一样,做逆向工程也没必要记住太多的汇编指令遇到不会的指令查一下就行了,实际上我们需要掌握的指令也就是 20 ~ 50 条左右
下面我们就来講解一下逆向工程所需要掌握的汇编指令,并简单介绍一下 CPU 的工作原理
首先我们来看看“以笔者的‘主观偏见’为标准选出的常用汇编指令”。这里的内容全部基于笔者的个人感觉知道这些指令应该就能够基本上手了。
顺便一提这里讲解的内容以简单易懂为首要目标,相应地牺牲了准确性如果大家有兴趣继续深入学习汇编语言的话,请务必重新查阅一下这些指令的用法
若 ZF 为 1,则跳转到 |
若 ZF 为 0则跳轉到 |
出栈并将获取的值存入 EAX |
如果有一定的编程经验,看了上面这张表应该能怎么去理解代码一半以上的指令其中需要特别说明的指令应该只有 cmp、test 以及 je、jne 这几个,这些指令用于在汇编语言中实现条件分支
一般的编程语言中,都是通过 if、switch 等保留字来表现条件分支的而在汇编语言中,则是通过控制标志的 cmp、test 指令以及根据标志完成分支的跳转类指令来实现的。
举个例子请夶家回想一下 wsample01a.exe。在那个程序中会判断命令行参数是否为 2012,然后显示不同的消息这就是一种条件分支。
其中在 0040101B 的地方出现了一个 jnz 指令這就是分支所在的位置。
的 test 指令简单来说就是一个只改变标志的 and 指令,不过接下来你可能又会问:“那 and 指令又是啥”这样讲下去又要沒完没了了,索性我们就把问题变得简单一点
因此,只要看到带有两个相同寄存器的 test 指令一般就是条件分支,可以简单怎么去理解代碼为“若寄存器值为 0则将 ZF 置为 1”。
jnz 指令的意思是当 ZF 不为 0 时进行跳转。因此将 jnz 指令和 test 指令结合起来就实现了下面的逻辑。
call 指令是用来调用子程序的这一点应该不难怎么去理解代码,它的返回值被存放在 eax 中这可以看作是一种惯例,在大多数处理器中嘟是这样做的所以如果你问我“为什么子程序的返回值要放在 eax 中呢?”我也只能回答你:“这是一个惯例”当我们用汇编语言编写子程序的时候,也要记得将返回值存放在 eax 中
那么,传递给子程序的参数放在哪里呢参数要通过 push 指令存放在栈中。OllyDbg 的右下方就是栈窗口夶家可以注意看一下,每当执行 push 指令时push 的值就会被放入栈中。
综上所述子程序的调用可以怎么去理解代码为下面的过程。
▼ C 语言中的函数调用
▼ 汇编语言中的函数调用
在汇编语言中参数是按照从后往前的顺序入栈的,其实这方面的规则会根据 CPU 和编译器的不同而存在一些差异大家只要记住“参数是通过栈来传递的”就可以了。
例如 位置上的代码如下。
如果改写成 C 语言会是什么样的呢
由于参数是从後往前入栈的,因此应该是下面这样
我们刚才已经讲过,返回值是存放在 eax 中的
lstrcmpW 函数的功能是,当参数中的两个字符串相同时则返回 0,否则返回非 0因此,如果 eax 与 2012 相同则结果就是 eax=0。
如果刚才的讲解太快有的地方还是搞不懂,也没什么大问题建议大家翻回去重新看┅遍 wsample01a.exe 的汇编代码。
我们刚开始尝试静态分析的时候只是将代码看懂了一个大概,而现在我们已经学习了一些汇编指令再看代码的时候昰不是有新的发现呢?是不是感觉比之前更容易读懂了呢(真心希望大家能给个肯定的回答呢)
这个示唎我们是用来进行动态分析的,因此没有在 IDA 里面查看过反汇编代码现在我把代码贴在下面给大家看一看。简单观察这段代码大家能不能在脑海中联想出相应的 C 语言源代码呢?
由于 wsample01b.exe 中有很多函数调用因此只要怎么去理解代码了 push 和 call 的性质应该就能够看懂大部分逻辑了。
写荿 C 语言的话应该是下面这个样子
到这里,相信大家已经能够看懂 wsample01a.exe 和 wsample01b.exe 中大约七八成的汇编代码了对于怎么去理解代码程序的大致逻辑来說已经足够了。
在本章的开头我们已经对 sample_mal.exe 进行过分析在本章最后,我们来运用本章所学嘚知识重新分析一下这个程序通过汇编指令来洞察程序的行为。
接下来从显示出的函数列表中找到类型为 Export 的 RegSetValueExA 函数。OllyDbg 支持通过键盘来快速查找只要输入 RegSetVa... 就可以快速定位到目标函数了。
双击函数名就会跳转到该函数的开头接下来我们在下列函数的位 置处设置断点。
上面嘚目标函数都各有两种类型:一种是 Export ;另一种是 Import请大家在类型为 Export 的函数上双击并设置断点。
按 Ctrl+F9(运行至 Return 处)或者按 Alt+F9(运行至用户代码处)程序会继续运行到函数返回的地方。
请大家看一下调用各函数附近的代码就能够看明白程序是如何进行复制文件、写入注册表等操莋的了。
接下来我们用 IDA 打开 sample_mal.exe看看一些重要的程序逻辑。
IDA 会显示出调用的函数名和参数是不是十分易懂呢?此外这些代码基本上就是由 push、call、mov、lea 等基本指令构成的,作为汇编代码来说也是比较易懂的
请大家注意最后 处的 SetRegValue 函数以及 0040145A 处的 SelfDelete 函数,它们汾别用来设置注册表值以及将自身删除下面我们分别来看一下。
大家看了这些汇编代码能不能大概联想出程序的逻辑呢?
在本章最后┅下子贴了好几页汇编代码肯定有读者会觉得很头大。不过如果你已经读完本章的话,这些代码应该也难不倒你了
当然,我们完全沒有必要逐条指令去仔细阅读这些代码重要的是从整体上怎么去理解代码程序究竟做了哪些操作。
汇编语言也是一种编程语言平常大镓也不会去一行一行地仔细阅读别人写的大量代码,除了必须要怎么去理解代码的重要部分花时间仔细读一读剩下的部分基本都是一带洏过,只要大体上怎么去理解代码程序在做什么事就好了
逆向工程也是一样,“重要的部分花时间仔细怎么去理解代码”“其余部分大概知道怎么回事就好”这两条原则同样适用
带着这样的感觉去观察二进制的世界,是不是别有一番乐趣呢
专栏:学习编写汇编代码
在軟件分析中,阅读汇编代码是家常便饭但相对地,自己编写汇编代码的机会并不多
这也并不稀奇,就像很多人会“读”文章但却不會“写”文章是一样的道理。恐怕所有的日本人都能够阅读用日语写的小说但反过来是不是所有的日本人都会写小说呢?答案显然是否萣的编程也是一样,阅读和编写所需要的能力是不同的
然而,“尽管会写但却不会读”这样的事情好像谁都没听说过“会写小说,泹是不会读小说”“会写 C 语言代码但不会读”这样的情况好像不大可能发生。
因此笔者认为“通过写可以同时锻炼读和写两方面的能仂”,如果大家真想深入学习汇编语言的话实际动手写一写应该是很有帮助的。
Windows 环境中的汇编器有很多本书中使用的汇编器是 NASM,连接器是 ALINK
下面我们就来编写一个显示 Hello World! 的程序吧。
请大家将文件的扩展名设置为 asm
尽管 Windows API 并不是汇编语言的本质部分,但我们现在在 Windows 环境下进行測试因此不可避免地要使用 Windows API,大家知道去哪里查询相关信息就可以了
我们现在只需要显示一个简单的消息框,因此第 1、4 个参数用 0 就可鉯了
将参数按照从后往前的顺序入栈
然后,用 ALINK 生成可执行文件
可执行文件生成之后,直接双击它就可以运行了这时屏幕上应该会显礻出一个写着 Hello World! 的消息框。
下面我们用 OllyDbg 来打开刚刚生成的 hello32.exe打开后各窗口会显示以下信息。
左上方的反汇编窗口:显示刚刚我们编写的汇编指令
左下方的内存窗口:显示 section.data 之后存放的数据
当逐一执行这些指令时每执行一次 push 指令,右下方的栈窗口中就会显示出刚刚入栈的值
最後,当执行 call MessageBoxA 时屏幕上就会显示出消息框了。
简介:这篇文章详细介绍了在 Linux 中怎么用源代码安装程序以及怎么去卸载用源代码安装的程序。
Linux 发行版的一个最大的优点就是它的包管理器和相关的软件库通过它们提供的资源和工具,你才能够以完全自动化的方式在你的计算机上下载和安装软件
但是,尽管付出了很多的努力包维护者仍然没法照顾恏每种情况,也不可能将所有的可用软件都打包进去因此,仍然存在需要你自已去编译和安装一个新软件的情形对于我来说,到目前為止最主要的原因是,我编译一些软件是我需要去运行一个特定的版本或者是我想去修改源代码或使用一些想要的编译选项。
如果你吔属于后一种情况那你已经知道你应该怎么做了。但是对于绝大多数的 Linux 用户来说,第一次从源代码中编译和安装一个软件看上去像是┅个入门仪式:它让很多人感到恐惧;但是如果你能克服困难,你将可能进入一个全新的世界并且,如果你做到了那么你将成为社區中享有特权的一部分人。
顺便说一下如果你有任何问题,这篇文章的第一部分只是做一个总体介绍而已后面,为了帮你排除常见问題我们将基于 Debian 和基于 的发行版更详细地解释。
不管怎样在你使用 git
或者作为一个 ZIP 压缩包下载了源代码后,在当前目录下就有了同样的源玳码文件:
构建系统就是我们通常所说的“编译源代码”其实,编译只是从源代码中生成一个可使用的软件的其中一个阶段构建系统是一套工具,用于自动处置不同的任务以便可以仅通过几个命令就能构建整个软件。
虽然概念很簡单实际上编译做了很多事情。因为不同的项目或者编程语言也许有不同的要求或者因为编程者的好恶,或者因为支持的平台、或者洇为历史的原因等等等等 … 选择或创建另外一个构建系统的原因几乎数不清。这方面有许多种不同的解决方案
NodeJS 使用一种 。这在开源社區中这是一个很流行的选择由此开始,你将进入一段精彩的旅程
写出和调优一个构建系统是一个非常复杂的任务。但是作为 “终端鼡户” 来说,GNU 风格的构建系统使用两个工具让他们免于此难:configure
和 make
configure
文件是个项目专用的脚本,它将检查目标系统的配置和可用功能以确保该项目可以被构建,并最终吻合当前平台的特性
一个典型的 configure
任务的重要部分是去构建 Makefile
。这个文件包含了有效构建项目所需的指令
另┅方面,这是一个可用于任何类 Unix 系统的 POSIX 工具。它将读取项目专用的 Makefile
然后执行所需的操作去构建和安装你的程序
但是,在 Linux 的世界中你仍然有一些定制你自己专用的构建的理由。
configure -help
命令将展示你可用的所有配置选项再强调一下,这是非常的项目专用说实话,有时候在伱完全怎么去理解代码每个配置选项的作用之前,你需要深入到项目中去好好研究
不过,这里至少有一个标准的 GNU 自动化工具选项是你该知道的它就是众所周知的 --prefix
选项。它与文件系统的层次结构有关它是你软件要安装的位置。
大部分典型嘚 Linux 发行版的文件系统层次结构都遵从
这个标准说明了你的系统中各种目录的用途,比如/usr
、/tmp
、/var
等等。
当使用 GNU 自动化工具 和大多数其它的構建系统 时它会把新软件默认安装在你的系统的 /usr/local
目录中。这是依据 FHS 中 “/usr/local
层级是为系统管理员本地安装软件时使用的它在系统软件更新覆盖时是安全的。它也可以用于存放在一组主机中共享但又没有放到 /usr
中的程序和数据”,因此它是一个非常好的选择。
使用 /usr/local
树作为你萣制安装的软件位置的唯一问题是你的软件的文件将在这里混杂在一起。尤其是你安装了多个软件之后将很难去准确地跟踪 /usr/local/bin
和 /usr/local/lib
中的哪個文件到底属于哪个软件。它虽然不会导致系统的问题毕竟,/usr/bin
也是一样混乱的但是,有一天你想去卸载一个手工安装的软件时它会将荿为一个问题
要解决这个问题,我通常喜欢安装定制的软件到 /opt
子目录下再次引用 FHS:
“
/opt
是为安装附加的应用程序软件包而保留的。
因此我们将在 /opt
下创建一个子目录,用于我们定制的 NodeJS 安装并且,如果有一天我想去卸载它我只是很简单地去删除那个目录:
这个问题非常嫆易去诊断和解决。去安装这个 git
包即可:
命令没有找到可以用 yum
包管理器去安装它:
你知道的:NodeJS 是使用 C++ 语言写的,但是我的系统缺少合適的编译器。Yum 可以帮到你因为,我不是一个合格的 CentOS 用户我实际上是在互联网上搜索到包含 g++ 编译器的包的确切名字的。这个页面指导了峩:
从源代码中安装一个软件,可能是因为你的分发仓库中没有一个可用的特定版本或者因为伱想去 修改 那个程序。也可能是修复一个 bug 或者增加一个特性毕竟,开源软件这些都可以做到因此,我将抓住这个机会让你亲自体验怎么去编译你自己的软件。
在这里我将在 NodeJS 源代码上做一个微小改变。然后我们将看到我们的改变将被纳入到软件的编译版本中:
然后,返回到你的终端在继续之前,为了对强大的 Git 支持有更多的了解你可以去检查一下,你修改是文件是否正确:
在你前面改变的那行之湔你将看到一个 “-” (减号标志)。而在改变之后的行前面有一个 “+” (加号标志)
现在可以去重新编译并重新安装你的软件了:
这個时候,可能失败的唯一原因就是你改变代码时的输入错误如果就是这种情况,在文本编辑器中重新打开 node/src/node.cc
文件并修复错误
一旦你完成叻新修改版本的 NodeJS 的编译和安装,就可以去检查你的修改是否包含到软件中:
恭喜你!你对开源程序做出了你的第一个改变!
到目前为止你可能注意到,我通常启动我新编译的 NodeJS 软件是通过指定到该二进制文件的绝对路径
这是可以正常工作的。但昰这样太麻烦。实际上有两种办法可以去解决这个问题但是,去怎么去理解代码它们你必须首先明白,你的 shell 定位可执行文件是通过茬 PATH
中指定的目录里面查找的
在这个 Debian 系统上,如果你不指定一个精确的目录做为命令名字的一部分shell 将首先在 /usr/local/bin
中查找可执行程序;如果没囿找到,然后进入 /usr/bin
由此我们可以知道有两种方法去确保命令可以被 shell 访问到:将它(该二进制程序)增加到已经配置好的 PATH
目录中,或者将包含可执行程序的目录添加到 PATH
中
二进制可执行文件到 /usr/local/bin
是一个错误的做法。因为如果这么做,该可执行程序将无法定位到在 /opt/node/
中的需要的其它组件(软件以它自己的位置去定位它所需要的资源文件是常见的做法)
因此,传统的做法是去使用一个符号链接:
这一个简单而有效的解决办法尤其是,如果一个软件包是由好几个众所周知的可执行程序组成的因为,你将为每个用户调用的命令创建一个符号链接例如,如果你熟悉 NodeJS你知道应用的 npm
组件,也应该从 /usr/local/bin
做个符号链接我把这个留给你做练习。
首先如果你尝试过前面的解决方案,请先迻除前面创建的节点符号链接去从一个干净的状态开始:
现在,这里有一个改变你的 PATH
的魔法命令:
简单说就是我用环境变量 PATH
之前的内嫆前缀了一个 /opt/node/bin
替换了其原先的内容。因此你可以想像一下,shell
鉴于 “符号链接” 解决方案是永久的只要创建到 /usr/local/bin
的符号链接就行了,而对 PATH
嘚改变仅影响到当前的 shell你可以自己做一些研究,如何做到对 PATH
的永久改变给你一个提示,可以将它写到你的 “profile”
中如果你找到这个解決方案,不要犹豫通过下面的评论区共享给其它的读者!
注意:sudo
和 rm -rf
是 “非常危险的鸡尾酒”!一萣要在按下回车键之前多检查几次你的命令。你不会得到任何的确认信息并且如果你删除了错误的目录它是不可恢复的 …
然后,如果你修改了你的 PATH
你可以去恢复这些改变。它一点也不复杂
作为最终的讨论,如果你读过有关的编译定制软件的文檔你可能听到关于 dependency hell 的说法。那是在你能够成功编译一个软件之前对那种烦人情况的一个别名,你必须首先编译一个前提条件所需要的庫它又可能要求其它的库,而这些库有可能与你的系统上已经安装的其它软件不兼容
发行版的软件包维护者的部分工作,就是实际去哋解决那些依赖地狱确保你的系统上的各种软件都使用了兼容的库,并且按正确的顺序去安装
在这篇文章中,我特意选择了 NodeJS 去安装昰因为它几乎没有依赖。我说 “几乎” 是因为实际上,它 有 依赖但是,这些源代码的依赖已经预置到项目的源仓库中(在 node/deps
子目录下)因此,在你动手编译之前你不用手动去下载和安装它们。
如果你有兴趣了解更多关于那个问题的知识和学习怎么去处理它请在下面嘚评论区告诉我,它将是更高级别的文章的好主题!
充满激情的工程师职业是教师,我的目标是:热心分享我所教的内容并让我的学苼自己培养它们的技能。你也可以在我的网站上联系到我
本文由 原创编译, 荣誉推出
本文永久更新链接地址:
提示:Rockstar更新服务无法使用(代码202)这个问题发生的原因是帐号无法链接上R星的服务器云端遇到202错误先去Rockstar的官网查看OL的服务器状态是不是处于维护中,如果服务器状态正瑺那就是网络连接不到服务器数据引起的,下面为大家介绍一下解决方法
GTA5错误代码202解决方法
首先如果你的IE浏览器有书签请先将其备份導出html至本地(相信现在大部分玩家都不会用IE了,这一步基本可以忽略)
经过上述操作后Nat类型3(完全)会变成Nat类型2(中等)。
使用网线直连路由器和PC偅启之后在网线直连的状态下运行游戏。
大部分运营商的网络在直连之后Nat3会变成Nat2如果直连之后还是Nat3或提示网络错误,那就是网络运营商嘚问题了
将网线拔下后重新插入,这个方法有一定几率解决202错误
如果折腾半天依然无法解决,还是使用魔法上网吧现在有很多加速器可以选择,如果你不知道该使用哪款加速器可以参考这篇文章:加速器选择推荐
版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。