如何查看二进制代码可执行代码是否可在x86上运行

调试逆向 【求助】怎么判断一段二进制数据里是否包含x86机器码? [文字模式]
- 看雪安全论坛
查看完整版本 : 调试逆向
hsluoyz我发现在OD里输入ABCDEF也能反汇编出东西来,甚至可以执行
adc dh,byte ptr ds:[esi+edx*2]
js Xcalc.0101242B
stos dword ptr es:[edi]
fdivr qword ptr ds:[edx+0x]
0 and dword ptr ds:[eax+0x],edx
那怎么判断一段二进制是x86机器码呢?或者说一段二进制里包含x86机器码,怎么找到x86机器码的起始位置呢?
chopsticks原则上对于一段指定的二进制数据是无法判断是否是可执行的机器码。
如你所述,对于123。。等这样数据,机器都会尝试着逐条解析为指令进行执行。。无非就是能不能解析出来,或者说解析出来后的指令是否有效的问题,但无法区分一般数据和机器码。
tihty如果能区分数据和代码,还会有那么多漏洞吗
hsluoyz额,最近在写论文,想从学术角度区分出代码来,比如检测网络数据包中的恶意代码?
正因为冯诺依曼当初没有区分数据和代码,所以我的研究才有意义呀。
能找着个差不多的算法就行,准确率90%就ok了,做学术没有100%的事情,吼吼~
foxabumachine learning?
whtyyA)提取feature
1、反汇编二进制数据为指令序列
2、提取频度信息。 add指令的比例,mov的比例,间接寻址的比例、寄存器使用频度等等维度的信息。
3、将 2种得到的feature,归一化到0.0~1.0区间,得到一个vector
B)采集模板样本集合
1、从现实世界中(各种可执行文件)使用A方法提取样本,得到样本集合R.
2、如果R是简单集合(比如都是从PE入口点提取指令),则可以计算出R的中心点(或者有几个中心点,即编译器特征)。如果R有明显的多个聚簇,则使用自动聚合方法寻找中心点。
3、2中得到的中心点集合,暂称为X,即为模板样本集合
对待判断的二进制数据Y,提取feature,得到vector
a,计算 a 与 X 中每个点的距离(d1,d2,d3,d4,...dn),如果d1~dn中存在小阀值的数,则认为Y是指令。具体匹配精度,由阀值据诶的那个
whtyy具体处理方法有很多,思路想通,其上仅为距离。实际应用有各种实际问题要处理,但写paper应该够了
whtyy再说一种更简单直接的处理思路,我没试过,不过应该只对稍微大点的数据块有用
A、对二进制数据提取feature
反汇编二进制数据,提取每种机器码的前缀的出现频度,得到vector a。
需要对指令翻译有些认识,不需要处理所有可能的机器码的前缀,大部分即可。对于X86指令:单字节指令/单字节扩展指令的第一个字节,双字节指令/双字节扩展指令的前两个字节,三字节指令的前3个字节。
判定方法:
对待判定的数据,按照A方法提取得到vector a,把a当成数据,计算信息熵值。对于指令数据(有意义序列)熵值应该偏低,对于非指令数据,熵值应该偏高。
这种方法,对于那些XOR之类的单表替换加密,是直接无视的。
零度x额,最近在写论文,想从学术角度区分出代码来,比如检测网络数据包中的恶意代码?
正因为冯诺依曼当初没有区分数据和代码,所以我的研究才有意义呀。
能找着个差不多的算法就行,准确率90%就ok了,做学术没有100%的事情,吼吼~
能找着个差不多的算法你的研究就有意义了吗?
wParmaA)提取feature
1、反汇编二进制数据为指令序列
2、提取频度信息。 add指令的比例,mov的比例,间接寻址的比例、寄存器使用频度等等维度的信息。
3、将 2种得到的feature,归一化到0.0~1.0区间,得到一个vector
B)采集模板样本集合
1、从现实世界中(各种可执行文件)使...
涛哥爱玩高科技还是同一台电脑,当跑着不同操作系统的时候,不关注最后生成的可执行文件的二进制格式,而是只看其中一条汇编指令汇编后的二进制代码(X86),那它在windows、linux下的二进制码是否一样?
相同的硬件,不同的操作系统,相同的汇编指令,相同的汇编器, 得到相同的 机器指令,也就是题主说的二进制代码(这里不考虑用于程序加载的辅助信息)是合理的,理由是:机器指令是直接给CPU执行的,而CPU是相同的,它所识别的机器指令集也是相同的,BIOS也是相同的,总之硬件部分都是相同的,和操作系统无关,所以编译成相同的机器指令是一种可以接受的结果。--------本人不懂汇编、不懂操作系统,以上回答,纯属扯淡,不要当真。
同一种汇编语言,翻译成的二进制代码是完全相同的,汇编语言的定义就是用助记符代替机器指令,注意是代替,纯粹的替换操作,所以,如果是同一种语言,在同一个硬件平台上,那么同样的汇编指令对应的二进制编码是完全相同的。NOP是0x90,那么不管是Linux还是Windows里,NOP都是0x90
不同编译器,不同选项,对“同一指令”处理结果可能会不同吧(这个我不是很确定,给出一个异议的思路,做参考,欢迎批评指正)。1. 跳转:比如 写 jmp lab 可能会根据优化情况优化成相对跳转或是决对跳转?(汇编器也是编译器,按照编译器介绍的内容这种情况是有可能的(如果不是反汇编出来的汇编指令))2. 如果汇编器有优化功能,优化级别高的话,那么写 mov a -& b
mov b -& c
会不会给你优化成 mov a -& c?(此条暂时未找到支持证据。)-----------------补充:3. 刚搜了一下,NASM 汇编器,调整优化参数 还会对立即数的处理有影响。1中提到的jmp 会有优化处理。(这里我指的机器码是机器码的“形式”,A。有时不同汇编里对 jmp 长跳转,短跳转,相对决定跳转的指令在写汇编程序时不强制区分,或有多种形式表现。B。同一长跳转jmp lab 可能是到0xffff,或长跳转 到 0xaaaa 暂认为他们相同(较真的话它们是不是也不算是相同的机器码?)。)
不断地寻找正确的方向本页面信息由华强电子网用户提供,如果涉嫌侵权,请与我们客服联系,我们核实后将及时处理。
应用与方案分类
摄像头让人们可以记录下镜头下的一切,同时也拉近了人们的距13964人阅读
陈硕 (giantchen_AT_gmail)
Blog.csdn.net/Solstice
本文主要讨论 Linux x86/x86-64 平台,偶尔会举 Windows 作为反面教材。
C/C++ 的二进制兼容性 (binary compatibility) 有多重含义,本文主要在&头文件和库文件分别升级,可执行文件是否受影响&这个意义下讨论,我称之为 library (主要是 shared library,即动态链接库)的 ABI (application binary interface)。至于编译器与操作系统的 ABI 留给下一篇谈 C++ 标准与实践的文章。
什么是二进制兼容性
在解释这个定义之前,先看看 Unix/C 语言的一个历史问题:open() 的 flags 参数的取值。open(2) 函数的原型是
int open(const char *pathname, int flags);
其中 flags 的取值有三个: O_RDONLY,& O_WRONLY,& O_RDWR。
与一般人的直觉相反,这几个值不是按位或
(bitwise-OR) 的关系,即 O_RDONLY | O_WRONLY != O_RDWR。如果你想以读写方式打开文件,必须用 O_RDWR,而不能用 (O_RDONLY | O_WRONLY)。为什么?因为 O_RDONLY, O_WRONLY, O_RDWR 的值分别是 0, 1, 2。它们不满足按位或
那么为什么 C 语言从诞生到现在一直没有纠正这个不足之处?比方说把 O_RDONLY, O_WRONLY, O_RDWR 分别定义为 1, 2, 3,这样 O_RDONLY | O_WRONLY == O_RDWR,符合直觉。而且这三个值都是宏定义,也不需要修改现有的源代码,只需要改改系统的头文件就行了。
因为这么做会破坏二进制兼容性。对于已经编译好的可执行文件,它调用 open(2) 的参数是写死的,更改头文件并不能影响已经编译好的可执行文件。比方说这个可执行文件会调用 open(path, 1) 来写
文件,而在新规定中,这表示读
文件,程序就错乱了。
以上这个例子说明,如果以 shared library 方式提供函数库,那么头文件和库文件不能轻易修改,否则容易破坏已有的二进制可执行文件,或者其他用到这个 shared library 的 library。操作系统的 system call 可以看成 Kernel 与 User space 的 interface,kernel 在这个意义下也可以当成 shared library,你可以把内核从 2.6.30 升级到 2.6.35,而不需要重新编译所有用户态的程序。
所谓&二进制兼容性&指的就是在升级(也可能是 bug fix)库文件的时候,不必重新编译使用这个库的可执行文件或使用这个库的其他库文件,程序的功能不被破坏。
见 QT FAQ 的有关条款:
在 Windows 下有恶名叫 DLL Hell,比如 MFC 有一堆 DLL,mfc40.dll, mfc42.dll, mfc71.dll, mfc80.dll, mfc90.dll,这是动态链接库的本质问题,怪不到 MFC 头上。
有哪些情况会破坏库的 ABI
到底如何判断一个改动是不是二进制兼容呢?这跟 C++ 的实现方式直接相关,虽然 C++ 标准没有规定 C++ 的 ABI,但是几乎所有主流平台都有明文或事实上的 ABI 标准。比方说 ARM 有 EABI,Intel Itanium 有
,x86-64 有仿 Itanium 的 ABI,SPARC 和 MIPS 也都有明文规定的 ABI,等等。x86 是个例外,它只有事实上的 ABI,比如 Windows 就是 Visual C++,Linux 是 G++(G++ 的 ABI 还有多个版本,目前最新的是 G++ 3.4 的版本),Intel 的 C++ 编译器也得按照 Visual C++ 或 G++ 的 ABI 来生成代码,否则就不能与系统其它部件兼容。
C++ ABI 的主要内容:
函数参数传递的方式,比如 x86-64 用寄存器来传函数的前 4 个整数参数
虚函数的调用方式,通常是 vptr/vtbl 然后用 vtbl[offset] 来调用
struct 和 class 的内存布局,通过偏移量来访问数据成员
name mangling
RTTI 和异常处理的实现(以下本文不考虑异常处理)
C/C++ 通过头文件暴露出动态库的使用方法,这个&使用方法&主要是给编译器看的,编译器会据此生成二进制代码,然后在运行的时候通过装载器(loader)把可执行文件和动态库绑到一起。如何判断一个改动是不是二进制兼容,主要就是看头文件暴露的这份&使用说明&能否与新版本的动态库的实际使用方法兼容。因为新的库必然有新的头文件,但是现有的二进制可执行文件还是按旧的头文件来调用动态库。
这里举一些源代码兼容但是二进制代码不兼容例子
给函数增加默认参数,现有的可执行文件无法传这个额外的参数。
增加虚函数,会造成 vtbl 里的排列变化。(不要考虑&只在末尾增加&这种取巧行为,因为你的 class 可能已被继承。)
增加默认模板类型参数,比方说 Foo 改为 Foo &,这会改变 name mangling
改变 enum 的值,把 enum Color { Red = 3 }; 改为 Red = 4。这会造成错位。当然,由于 enum 自动排列取值,添加 enum 项也是不安全的,除非是在末尾添加。
给 class Bar 增加数据成员,造成 sizeof(Bar) 变大,以及内部数据成员的 offset 变化,这是不是安全的?通常不是安全的,但也有例外。
如果客户代码里有 new Bar,那么肯定不安全,因为 new 的字节数不够装下新 Bar。相反,如果 library 通过 factory 返回 Bar* (并通过 factory 来销毁对象)或者直接返回 shared_ptr,客户端不需要用到 sizeof(Bar),那么可能是安全的。 同样的道理,直接定义 B 对象(无论是函数局部对象还是作为其他 class 的成员)也有二进制兼容问题。
如果客户代码里有 Bar* pB pBar-&memberA =,那么肯定不安全,因为 memberA 的新 Bar 的偏移可能会变。相反,如果只通过成员函数来访问对象的数据成员,客户端不需要用到 data member 的 offsets,那么可能是安全的。
如果客户调用 pBar-&setMemberA(xx); 而 Bar::setMemberA() 是个 inline function,那么肯定不安全,因为偏移量已经被 inline 到客户的二进制代码里了。如果 setMemberA() 是 outline function,其实现位于 shared library 中,会随着 Bar 的更新而更新,那么可能是安全的。
那么只使用 header-only 的库文件是不是安全呢?不一定。如果你的程序用了 boost 1.36.0,而你依赖的某个 library 在编译的时候用的是 1.33.1,那么你的程序和这个 library 就不能正常工作。因为 1.36.0 和 1.33.1 的 boost::function 的模板参数类型的个数不一样,其中一个多了 allocator。
这里有一份黑名单,列在这里的肯定是二级制不兼容,没有列出的也可能二进制不兼容,见 KDE 的文档:
哪些做法多半是安全的
前面我说&不能轻易修改&,暗示有些改动多半是安全的,这里有一份白名单,欢迎添加更多内容。
只要库改动不影响现有的可执行文件的二进制代码的正确性,那么就是安全的,我们可以先部署新的库,让现有的二进制程序受益。
增加新的 class
增加 non-virtual 成员函数
修改数据成员的名称,因为生产的二进制代码是按偏移量来访问的,当然,这会造成源码级的不兼容。
还有很多,不一一列举了。
反面教材:COM
在 C++ 中以虚函数作为接口基本上就跟二进制兼容性说拜拜了。具体地说,以只包含虚函数的 class (称为 interface class)作为程序库的接口,这样的接口是僵硬的,一旦发布,无法修改。
比方说 M$ 的 COM,其 DirectX 和 MSXML 都以 COM 组件方式发布,我们来看看它的带版本接口 (versioned interfaces):
IDirect3D7, IDirect3D8, IDirect3D9, ID3D10*, ID3D11*
IXMLDOMDocument, IXMLDOMDocument2, IXMLDOMDocument3
话句话说,每次发布新版本都引入新的 interface class,而不是在现有的 interface 上做扩充。这样不能兼容现有的代码,强迫客户端代码也要改写。
回过头来看看 C 语言,C/Posix 这些年逐渐加入了很多新函数,同时,现有的代码不用修改也能运行得很好。如果要用这些新函数,直接用就行了,也基本不会修改已有的代码。相反,COM 里边要想用 IXMLDOMDocument3 的功能,就得把现有的代码从 IXMLDOMDocument 全部升级到 IXMLDOMDocument3,很讽刺吧。
tip:如果遇到鼓吹在 C++ 里使用面向接口编程的人,可以拿二进制兼容性考考他。
采用静态链接
这个是王道。在分布式系统这,采用静态链接也带来部署上的好处,只要把可执行文件放到机器上就行运行,不用考虑它依赖的 libraries。目前 muduo 就是采用静态链接。
通过动态库的版本管理来控制兼容性
这需要非常小心检查每次改动的二进制兼容性并做好发布计划,比如 1.0.x 系列做到二进制兼容,1.1.x 系列做到二进制兼容,而 1.0.x 和 1.1.x 二进制不兼容。《程序员的自我修养》里边讲过 .so 文件的命名与二进制兼容性相关的话题,值得一读。
用 pimpl 技法,编译器防火墙
在头文件中只暴露 non-virtual 接口,并且 class 的大小固定为 sizeof(Impl*),这样可以随意更新库文件而不影响可执行文件。当然,这么做有多了一道间接性,可能有一定的性能损失。见 Exceptional C++ 有关条款和 C++ Coding Standards 101.
Java 是如何应对的
Java 实际上把 C/C++ 的 linking 这一步骤推迟到 class loading 的时候来做。就不存在&不能增加虚函数&,&不能修改 data member& 等问题。在 Java 里边用面向 interface 编程远比 C++ 更通用和自然,也没有上面提到的&僵硬的接口&问题。
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:2113879次
积分:16299
积分:16299
排名:第175名
原创:141篇
评论:3131条
本人博客的文章均为原创作品,除非另有声明。个人转载或引用时请保留本人的署名及博客网址,商业转载请事先联系。我不使用即时聊天工具。也请不要用 CSDN 站内信、短消息或者留言功能跟我联系。我的 gmail 用户名是 giantchen,微博 /giantchen。
(1)(1)(1)(2)(1)(3)(1)(1)(4)(1)(1)(1)(2)(2)(1)(2)(2)(3)(6)(8)(5)(10)(1)(4)(3)(1)(3)(2)(3)(4)(1)(1)(1)(1)(1)(1)(1)(1)(1)(1)(1)(1)(2)(1)(1)(4)(5)(3)(7)(1)(2)(3)(5)(2)(2)(4)(6)(1)(3)(1)(1)

我要回帖

更多关于 二进制代码 的文章

 

随机推荐