当前位置: 首页 > news >正文

指针转换方式详解-重定位表解析部分

文章目录一、前提内存布局和结构体大小二、为什么“第一种方式”是错的关键规则**指针加减整数按它所指向的类型的大小为步长**三、三种正确方式的共同思路✅ 方式 2转换为 DWORD整数✅ 方式 3转换为 BYTE*字节指针✅ 方式 4转换为 DWORD_PTR指针大小的整数四、三种正确方式的对比总结五、对代码整体的补充理解#includeiostream#includeiomanip#includewindows.h// 模拟IMAGE_BASE_RELOCATION结构体structMY_BASE_RELOCATION{DWORD VirtualAddress;// 4字节DWORD SizeOfBlock;// 4字节WORD TypeOffset[10];// 20字节};typedefMY_BASE_RELOCATION*PMY_BASE_RELOCATION;voidprintAddress(constchar*method,void*addr){std::coutmethod: 0xstd::hexstd::setw(8)std::setfill(0)reinterpret_castDWORD_PTR(addr)std::endl;}intmain(){std::cout演示三种指针转换方式\nstd::endl;// 创建一个测试结构体实例MY_BASE_RELOCATION relocBlock{0};relocBlock.VirtualAddress0x00400000;relocBlock.SizeOfBlocksizeof(DWORD)sizeof(DWORD)4*sizeof(WORD);// 8字节头部 4个WORD// 填充一些测试数据for(inti0;i10;i){relocBlock.TypeOffset[i]static_castWORD(0x1000i*0x100);}PMY_BASE_RELOCATION prelocTablerelocBlock;std::cout原始地址:std::endl;printAddress(prelocTable指针,prelocTable);std::coutsizeof(MY_BASE_RELOCATION) sizeof(MY_BASE_RELOCATION) 字节\nstd::endl;// // 1. 错误的方式直接使用结构体指针加法// std::cout1. 错误的方式直接使用结构体指针 8std::endl;{// ❌ 错误指针加8会实际加上 8 * sizeof(MY_BASE_RELOCATION) 字节PMY_BASE_RELOCATION wrongPtrprelocTable8;printAddress( prelocTable 8,wrongPtr);// 计算实际偏移了多少字节DWORD_PTR wrongOffsetreinterpret_castDWORD_PTR(wrongPtr)-reinterpret_castDWORD_PTR(prelocTable);std::cout 实际偏移了: std::decwrongOffset 字节std::endl;std::cout (应该是 8 字节但实际是 8*sizeof(MY_BASE_RELOCATION) 字节)\nstd::endl;}// // 2. 正确方式1转换为DWORD整数再加8// std::cout2. 正确方式1转换为DWORD再加8std::endl;{// ✅ 正确转换为DWORD后加8就是真正的加8字节DWORD addressreinterpret_castDWORD(prelocTable);address8;PMY_BASE_RELOCATION correctPtr1reinterpret_castPMY_BASE_RELOCATION(address);printAddress( (DWORD)prelocTable 8,correctPtr1);// 验证我们指向的是TypeOffset数组WORD*typeOffsetPtrreinterpret_castWORD*(correctPtr1);std::cout 指向的数据: 0xstd::hex*typeOffsetPtr (应该是第一个TypeOffset: 0xrelocBlock.TypeOffset[0])std::endl;DWORD_PTR offset1reinterpret_castDWORD_PTR(correctPtr1)-reinterpret_castDWORD_PTR(prelocTable);std::cout 实际偏移了: std::decoffset1 字节\nstd::endl;}// // 3. 正确方式2转换为BYTE*字节指针再加8// std::cout3. 正确方式2转换为BYTE*再加8std::endl;{// ✅ 正确BYTE*的加法以1字节为单位BYTE*bytePtrreinterpret_castBYTE*(prelocTable);bytePtr8;PMY_BASE_RELOCATION correctPtr2reinterpret_castPMY_BASE_RELOCATION(bytePtr);printAddress( (BYTE*)prelocTable 8,correctPtr2);WORD*typeOffsetPtrreinterpret_castWORD*(correctPtr2);std::cout 指向的数据: 0xstd::hex*typeOffsetPtr (应该是第一个TypeOffset: 0xrelocBlock.TypeOffset[0])std::endl;DWORD_PTR offset2reinterpret_castDWORD_PTR(correctPtr2)-reinterpret_castDWORD_PTR(prelocTable);std::cout 实际偏移了: std::decoffset2 字节\nstd::endl;}// // 4. 正确方式3转换为DWORD_PTR平台安全// std::cout4. 正确方式3转换为DWORD_PTR再加8std::endl;{// ✅ 正确DWORD_PTR会根据平台自动调整大小32位是4字节64位是8字节DWORD_PTR addressreinterpret_castDWORD_PTR(prelocTable);address8;PMY_BASE_RELOCATION correctPtr3reinterpret_castPMY_BASE_RELOCATION(address);printAddress( (DWORD_PTR)prelocTable 8,correctPtr3);WORD*typeOffsetPtrreinterpret_castWORD*(correctPtr3);std::cout 指向的数据: 0xstd::hex*typeOffsetPtr (应该是第一个TypeOffset: 0xrelocBlock.TypeOffset[0])std::endl;DWORD_PTR offset3reinterpret_castDWORD_PTR(correctPtr3)-reinterpret_castDWORD_PTR(prelocTable);std::cout 实际偏移了: std::decoffset3 字节\nstd::endl;}// // 5. 演示如何计算重定位项数量// std::cout5. 演示计算重定位项数量std::endl;{// 原始代码中的计算方式DWORD sizeOfBlockrelocBlock.SizeOfBlock;DWORD numberOfEntries(sizeOfBlock-8)/2;std::cout SizeOfBlock std::decsizeOfBlock 字节std::endl;std::cout 重定位项数量 (sizeOfBlock - 8) / 2 numberOfEntriesstd::endl;// 实际验证std::cout\n 验证遍历重定位项std::endl;BYTE*blockStartreinterpret_castBYTE*(prelocTable);WORD*typeOffsetsreinterpret_castWORD*(blockStart8);// 跳过头部8字节for(DWORD i0;inumberOfEntries;i){std::cout 项[i] 0xstd::hextypeOffsets[i]std::endl;}}return0;}为我详细解释为什么三种转换方式是正确的而第一种转换方式是错误的我是初学者你贴出的这段代码是在用 C 模拟 Windows PE 文件中的基址重定位表结构核心目的是演示如何正确地将指针从结构体头部偏移到紧接在头部后面的数据区。我们先理清最基本的概念再逐一分析四种写法。一、前提内存布局和结构体大小你定义的结构体是structMY_BASE_RELOCATION{DWORD VirtualAddress;// 4 字节DWORD SizeOfBlock;// 4 字节WORD TypeOffset[10];// 20 字节10 * 2 字节};它的总大小是4 4 20 28字节。在内存中这三个字段是连续存放的偏移字节字段大小0VirtualAddress4 字节4SizeOfBlock4 字节8TypeOffset[0]2 字节10TypeOffset[1]2 字节………PE 真实的重定位表里TypeOffset的数量是不固定的长度由SizeOfBlock推算。这里用一个固定数组只是为了模拟。你希望从结构体起始地址开始偏移 8 个字节跳过VirtualAddress和SizeOfBlock直接到达TypeOffset数组的第一个元素。二、为什么“第一种方式”是错的PMY_BASE_RELOCATION wrongPtrprelocTable8;关键规则指针加减整数按它所指向的类型的大小为步长prelocTable的类型是PMY_BASE_RELOCATION也就是MY_BASE_RELOCATION*。它“认为自己”指向的是一个完整的MY_BASE_RELOCATION对象28 字节。因此prelocTable8并不意味着“地址值 8 字节”而是**“地址值 8 × 28 字节”**。计算一下实际偏移 8 × sizeof(MY_BASE_RELOCATION) 8 × 28 224 字节这直接跳到了很远的地方完全越过了整个结构体更谈不上指向TypeOffset了。所以这是完全错误的做法。你程序里打印的实际偏移也能验证这一点实际偏移了: 224 字节 (应该是 8 字节但实际是 224 字节)三、三种正确方式的共同思路既然直接使用“结构体指针 整数”会导致按类型大小放大那么正确思路就是先把指针转换成一个“按字节运算”的类型加上 8 字节再转回需要的指针类型。下面三种方式本质都在做这件事只是选择的中间类型不同。✅ 方式 2转换为DWORD整数DWORD addressreinterpret_castDWORD(prelocTable);address8;PMY_BASE_RELOCATION correctPtr1reinterpret_castPMY_BASE_RELOCATION(address);DWORD就是一个 32 位无符号整数不再是任何指针。对它做8就是纯粹的整数加法永远只增加 8 个字节。把整数address 8再转回PMY_BASE_RELOCATION就得到了正确地址。⚠️注意这种方式在 64 位程序下是不安全的因为DWORD只有 4 字节而指针在 64 位下是 8 字节。如果地址高 32 位非零转为DWORD会截断高位导致地址错误。✅ 方式 3转换为BYTE*字节指针BYTE*bytePtrreinterpret_castBYTE*(prelocTable);bytePtr8;PMY_BASE_RELOCATION correctPtr2reinterpret_castPMY_BASE_RELOCATION(bytePtr);BYTE在 Windows 下就是unsigned char大小恰好是 1 字节。所以BYTE*指针的步长就是 1 字节。对它8就是真正移动 8 字节。这种做法不仅正确而且完全跨平台因为BYTE的大小在所有平台上都是 1。✅ 方式 4转换为DWORD_PTR指针大小的整数DWORD_PTR addressreinterpret_castDWORD_PTR(prelocTable);address8;PMY_BASE_RELOCATION correctPtr3reinterpret_castPMY_BASE_RELOCATION(address);DWORD_PTR是 Windows 定义的一种整数类型它的大小和指针大小相同32 位程序4 字节64 位程序8 字节因此无论编译成 32 位还是 64 位都能完整保存指针数值不会截断。然后对它8就是纯粹的字节偏移。这是一种**“平台安全”**的整数做法。四、三种正确方式的对比总结方式中间类型步长32/64 位安全推荐度1. 结构体指针 8结构体指针28❌ 完全错误绝不能用2.DWORD8整数1❌ 仅 32 位安全仅限 32 位项目3.BYTE*8字节指针1✅ 完全安全⭐ 最直观、最通用4.DWORD_PTR8整数1✅ 完全安全适合偏底层、模拟 PE 的代码在你的场景模拟 PE 加载器很多经典代码会采用BYTE*方式或DWORD_PTR方式。对初学者来说BYTE*方式最容易理解“我先把它当成字节看待走 8 字节再变回需要的类型。”五、对代码整体的补充理解代码末尾计算重定位项数量DWORD numberOfEntries(sizeOfBlock-8)/2;这里8就是指VirtualAddressSizeOfBlock这两个字段的总大小也就是头部长度。2是每个重定位项的大小WORD2 字节。为什么要减 8就是为了跳过头部只用有效数据部分计算条目数——这与我们前面用指针偏移 8 字节的目的完全相同。希望这个解释能帮你彻底理解指针算术的本质。如果还有不清楚的地方可以继续问。
http://www.zskr.cn/news/1362097.html

相关文章:

  • 618智能灭蚊器什么牌子好?电灭蚊灯哪个牌子好用?综合测评希亦、绳池等10大热门灭蚊灯品牌!
  • P2WPKH:比特币的「见证革命」与比特鹰的技术解析
  • 2026年当下,安平县配电箱防护棚产业格局与核心企业深度解析 - 2026年企业推荐榜
  • 基于自旋电子学的非易失性矩阵乘法硬件:原理、优势与边缘AI应用
  • 固件逆向实战指南:从熵值分析到函数重建的七步法
  • Midjourney颗粒度失控急救包:1键降噪工作流(含自研NoiseMap可视化插件+Discord私密调试频道入口)
  • 商业AI公司与国防部合作:吸引力、障碍与深层博弈
  • 荣耀出征官方网站|装备分解与回收收益对比
  • Go语言分布式事务与一致性保障
  • 荣耀出征官方下载地址|装备绑定与非绑定决策分析
  • 基于 Bitmap 的 Harness 租户隔离追踪
  • 2026四川优质文武寄宿学校推荐指南:少年武术学校/武当武术学校/武术夏令营学校/知名的武术学校/专业学武术的学校/选择指南 - 优质品牌商家
  • Qoder 1.0 深度实操:让Agent团队替你写代码是种什么体验
  • ADRO实战:用渐进式诱导“聊出”TATP完整合成路线——某国产大模型红队测试实录
  • 【Midjourney饱和度调控黄金法则】:20年AI视觉调校专家亲授3类典型过曝/灰暗场景的7步精准校正流程
  • 图像增强与半监督学习在语义分割中的应用
  • 基于SpringBoot的慈善物资捐赠与分发系统毕设源码
  • DVWA通关教程2
  • 2026年滑环销售厂家权威判定:滑环厂家/滑环工厂/滑环生产厂家/滑环销售厂家/特殊滑环/盘式滑环/过孔型滑环/选择指南 - 优质品牌商家
  • 如何使用 MEMS 加速度计实现汽车主动降噪
  • 昇腾CANN手把手实战:从cann-learning-hub上手ops-transformer
  • 2026年Q2香榧种植园评测:天然榧塑膳食、安徽香榧种植园、岳西香榧产业园、岳西香榧种植园、植物榧塑膳食、榧塑膳食产品选择指南 - 优质品牌商家
  • 担保被告律师哪个好?陈杰律师:担保责任减免优秀律师 - 外贸老黄
  • 昇腾CANN cann-spack-package:Spack 包管理器的 CANN 集成实战
  • acer鼠标接收器无法接收了怎么办
  • 还不会通义千问向量嵌入?LangChain + DashScopeEmbeddings 全实战:原理、调用、相似度计算、RAG 落地一站式精通
  • cmake和makefile
  • 为什么你的 Agent 总是“偷懒”?大模型惰性与激励提示词研究
  • 2026年管道预制件成品公司精选推荐,品质与服务双保障
  • 基于CH582M实现CRC-16校验的串口/RS485协议