PE的基本概念
PE(Portable Execute)文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任何扩展名。
PE文件并不是作为单一内存映射文件被装入内存,Windows加载器(又称PE加载器)遍历PE文件并决定文件的哪一部分被映射,这种映射方式是将文件较高的偏移位置映射到较高的内存地址中。PE文件的结构在磁盘和内存中是基本一样的,但在装入内存中时又不是完全复制。Windows加载器会决定加载哪些部分,哪些部分不需要加载。而且由于磁盘对齐与内存对齐的不一致,加载到内存的PE文件与磁盘上的PE文件各个部分的分布都会有差异。
PE文件至少包含两个段,即数据段和代码段。Windows NT 的应用程序有9个预定义的段,分别为 .text 、.bss 、.rdata 、.data 、.pdata 和.debug 段,这些段并不是都是必须的,当然,也可以根据需要定义更多的段(比如一些加壳程序)。
在应用程序中最常出现的段有以下6种:
.执行代码段,通常 .text (Microsoft)或 ...
GCC交叉编译
使用 GCC 在单一的构建机器上来为不同的 CPU 架构交叉编译二进制文件。
如果你是一个开发者,要创建二进制软件包,像一个 RPM、DEB、Flatpak 或 Snap 软件包,你不得不为各种不同的目标平台编译代码。典型的编译目标包括 32 位和 64 位的 x86 和 ARM。你可以在不同的物理或虚拟机器上完成你的构建,但这需要你为何几个系统。作为代替,你可以使用 GNU 编译器集合 (GCC) 来交叉编译,在单一的构建机器上为几个不同的 CPU 架构产生二进制文件。
假设你有一个想要交叉编译的简单的掷骰子游戏。在大多数系统上,以 C 语言来编写这个相对简单,出于给添加现实的复杂性的目的,我以 C++ 语言写这个示例,所以程序依赖于一些不在 C 语言中东西 (具体来说就是 iostream)。
12345678910111213141516171819202122232425262728293031323334353637#include <iostream>#include <cstdlib>using namespace std;v ...
ollvm原理
Obfuscator-LLVM
Ollvm大致可分为 bcf(虚假块), fla(控制流展开/扁平化), sub(指令膨胀), Split(基本块分割)
bcf:
克隆一个真实块,并随机替换其中的一些指令,然后用一个永远为真的条件建立一个分支。克隆后的块是不会被执行的。
Fla:
将所有的真实块使用一个switch case结构包裹起来,每个真实块执行完毕后都会重新赋值switch var,对于有分支的块会使用select指令,并跳转到switch起始代码块(分发器)上,根据switch var来执行下一个真实块。
Sub:
指令膨胀,将一条运算指令,替换为多条等价的运算指令。
Split:
利用随机数产生分割点,将一个基本块分割为两个,并使用绝对跳转连接起来。
关于ollvm具体的实现,可参考源码。
指令替换 -mllvm -sub
虚假控制流 -mllvm -bcf
控制流展平 -mllvm -fla
函数(Funtions)注解
还原思路
网上有很多还原ollvm的脚本,但是只能还原特征很明显的ollvm,或者说只是debug版的ollvm。在debug版中o ...
ELF程序头
程序头表提供了二进制文件的段视图,与节头表提供的节视图相反。早前讨论过的ELF二进制文件的节视图仅适用于静态链接。相比之下,下面我们将要讨论的是段视图。在将ELF二进制文件加载到进程并执行的时候,定位相关代码和数据并确定加载到虚拟内存中的内容时,操作系统和动态链接器就会用到段视图。
ELF段包含零个或多个节,实际上就是把多个节捆绑成单个块。段提供的可执行视图,只有ELF二进制文件会用到它们,而非二进制文件(如可重定位对象)则用不到它们。
程序头表使用Elf64_Phdr结构体类型的程序头对段视图进行编码,每个程序头均包含清单2-11所示的字段。
p_type字段
p_type字段标识了段的类型,该字段的重要类型包括PT_LOAD、PT_DYNAMIC及PT_INTERP。
PT_LOAD类型的段会在创建进程时加载到内存中,程序头的剩余部分描述了可加载块的大小和将其加载到的地址。
通常至少有两个PT_LOAD类型的段,一个包含不可写数据节,另一个包含可写数据节。
PT_INTERP类型的段包含了.interp节,该节提供了加载二进制文件的解释器的名称。 ...
ASCII码表
ASCII(American Standard Code for Information Interchange,美国信息互换标准代码)是一套基于拉丁字母的字符编码,共收录了 128 个字符,用一个字节就可以存储,它等同于国际标准 ISO/IEC 646。ASCII 规范于 1967 年第一次发布,最后一次更新是在 1986 年。
ASCII编码范围0x00-0x7F,即十进制的0-127,定义了128个单字节字符。它包含了 33 个控制字符(具有某些特殊功能但是无法显示的字符)和 95 个可显示字符(数字、字母、符号)。国标码GB18030、国际码Unicode均兼容ASCII编码。
第一部分:ASCII非打印控制字符,0~31及127(共33个)是控制字符或通信专用字符
第二部分:ASCII打印字符,32~126(共95个)是字符(32sp是空格),其中48~57为0到9十个阿拉伯数字;65~90为26个大写英文字母,97~122号为26个小写英文字母,其余为一些标点符号、运算符号等。
第三部分:扩展ASCII打印字符,后128个称为扩展ASCII码,目前 ...
二进制分析
未读1.Acid burn
寻找MessageBox
我们的目的是找到序列号或者序列号的计算规则,分析的目标是找到正确的目标函数,再分析函数的功能。首先用onlydbg加载并调试程序,点击check it,弹出窗口,我们要去定位窗口调用的位置,因为我们需要依据这个MessageBox定位判断函数;
搜索所有模块间的调用,
找到MessageBoxA,在所有调用设置断点
我们可以分析出弹窗调用发生在地址0x42A1A9 call <jmp.&user32.MessageBoxA> ; \MessageBoxA
追溯判断逻辑
保留这个断点,再次运行程序,点击check it! 可以发现在右下角堆栈处找到最近一条Return语句:
0019F704 |0042FB37 返回到 Acid_bur.0042FB37 来自 Acid_bur.0042A170
右键Follow in Disassm…(反汇编跟随),
如下代码:
12345678910111213141516171819202122232425262728290042FACD |. E8 ...
二进制分析
未读2.Afkayas1
运行程序,崩溃,缺少msvbvm50.dll,程序无法运行。没办法,上网上搜索一个,放到程序同一级目录,再次运行,OK。
输入name和serial,
弹出对话框,不要点击确定也不要关闭,我们去堆栈查看调用,MsgBox的位置,显示调用;
可以看到,再往上面一点就看到可读的字符串了,程序的主要逻辑就在这附近。
断点调试,可以发现序列号AKA-585291
此位置,ECX获得了用户名,
0x8EE4B就是585291,计算过程是
加上前缀AKA-
OllyDbg是一个32位的动态调试器,在平常做逆向的题中用的比较多,下面用bugku一个简单的例子Eazy-Re来介绍一下OllyDbg的使用。
首先打开程序,看一下是干什么的,他提示你输入flag,这里我随便输入几个字母,提示我不正确。
我们用OD打开程序,会看到下面的这个样子,如果没接触过OD的人可能直接被劝退了,这是啥乱七八糟的,别急,一点点来看,打开程序后会出现5个面板(我更喜欢称之为窗口),分别是反汇编窗口,信息窗口,寄存器窗口,数据窗口,栈窗口。
反汇编窗口
反汇编窗口显示被调试程序的代码,总共有4列,分别是地址,十六进制的机器码,反汇编代码和注释。在最后一列注释中显示了相关API参数或运行简表,非常有用。
在反汇编窗口的列中,默认情况下,双击可以完成如下操作:
地址列:显示被双击行地址的相对地址,再次双击返回标准地址模式
十六进制机器码列:设置或取消断点,对应的快捷键是F2
反汇编代码列:调用汇编器,可以直接修改汇编代码,对应的快捷键是空格键。
注释列:允许增加或编辑注释,对应的快捷键是;。
信息窗口
在进行动态跟踪时,信息窗口将显示与指令相关的各寄 ...
二进制分析
未读3.Afkayas2
同1时类似,依旧是找到MsgBox函数,右键显示调用;
找到的序列号为1600318,完全没有章法;
直接单步运行,记录寄存器变化;
用户名:itachi,长度6,第一个字符ascii码为0x69,
第一次计算:len(s)*0x15B38 + ascii(first(s)),值为533433
533433-> 533435->1600303->1600318
第一次浮点计算
12345678910111213141516171819202122232425262728293031323334004082D7 . FF15 18B14000 call dword ptr ds:[<&MSVBVM50.__vbaHresu>; msvbvm50.__vbaHresultCheckObj004082DD > 8B8D 58FFFFFF mov ecx,dword ptr ss:[ebp-0xA8]004082E3 . 8B55 E8 mov edx,dword ptr ss:[ebp-0 ...
利用缓冲区溢出来执行任意代码
1、缓冲区溢出示例
缓冲区溢出(buffer overflow):最有名的漏洞之一,输入的数据超出了程序规定的内存范围,数据溢出导致程序发生异常。
eg.
12345678#include <string.h>int main(int argc, char *argv[]){ char buff[64]; strcpy(buff, argv[1]); return 0;}
这个程序为 buff 数组分配了一块 64 字节的内存空间,但传递给程序的 参数 argv[1] 是由用户任意输入的,因此参数的长度很有可能会超过 64 字节
因此,当用户故意向程序传递一个超过 64 字节的字符串时,就会在 main 函数中引发缓冲区溢出。
2、让普通用户用ROOT权限运行程序
setuid :让用户使用程序的所有者权限来运行程序
一个 sample
1234567891011121314#include <unistd.h>#include <sys/types.h>int main(int a ...