《修改进程常量的几种方法.docx》由会员分享,可在线阅读,更多相关《修改进程常量的几种方法.docx(8页珍藏版)》请在三一办公上搜索。
1、1. 引言本文笔者在研究某著名软件的安全性时,碰到要修改进程中只读数据段(.rdata冲的常量 值的场景。笔者将这方的方法技巧及相关的经验向广大读者朋友进行分享,同时由于笔者技术水平有限,本文中若有错误恳请不吝赐教)。图-1修改常量值的程序代码修改常量值,为避免编译t昔误,直 接修改内存而不能Kffio为了简单的说明问题,笔者使用delph 1写了一个小示例程序(其实,使用什么语言并不 重要,本示例程序只是为了分析问题,并验证问题解决思路是否正确,从严格意义来说该示 例也有许多不严谨之处,例如因为编译器不同对字符串常量的处理也是不同的,但这些细节 问题我们暂时可以忽略),在本示例程序中,声明了
2、一个全局常量,在程序中动态修该常量 值,看看运行下过如何。write Log (1 =改p Co list := 0G_Name 1;write Log (1常的地址:twrite Log ( t G_Narn& ;.一 wrj匕已Lou L)F始 普竦艳”址日 Naiumm肖+,wri teLog C m-恐可而T日段示倒稗L HH1 KEl Mfil *LMB LJ-25 Q9 06】 LA03 L3:咨网 411 LCr&5 L3 5方仙相 LCHJ3; L3 5 细5 LCrOi l3.S_ 44tl*a3H!iit: iee 盾M配 Ibmllllthii it 00Mt TklEH
3、NW! H Hs 夺值 *(Aec*xx -vialitidn *L *ldr*xxtn no did* Bfi it aiWmo. kkb Kritt of tjddr*ii. UHE04修改常量值报写内存措误作杆差.眼墨谨同黄却遂鲂IS布充,姓和TFft丈*抵雷史EW击 QQ: 7Q75W12.图-2运行程序修改常量值报写内存错误如图-2所示,运行示例程序修改常量值,会报写内存错误。当然,本示例只是修改本进 程中的常量值,在实际的生产环境没有太大的实际意义,笔者使用该示例只是聚焦问题本质, 简化实际场景。在软件安全研究领域,经常需要修改第三方进程注入常量的只读数据中的数 据,分析思路和方法
4、是相同的,笔者接下来,将围绕该示例对报错原因进行深入分析,并且 给出该问题的解决方法。2. 问题分析本小节笔者对在进程运行期间修改常量值报写内存错误的原因进行分析。笔者曾看到过 一篇博客博客,其作者认为常量的只是在编译期间进行检查其值是否发生变化,在运行期间 常量的值是可以修改的。这个观点是错误的(至少来说是不准确的)。在程序运行期间常量 的值也是不可以修改的,那么这是如何做到的呢?在进程中一般都含有三个S(section),代码段(.text),数据段(.data),只读数据段(.rdata), 段的名称会因为编译器不同而有差异,微软的C,C+编译器一般将代码段命名为.text”,数 据段名
5、为“.data”,只读数据段命名为.rdata”。Borlad的pascal和C+编译器将代码段名 为”CODE”,数据段命名为DATA”,只读数据段名为”.rdata”。代码段(.text)的访问控制属性是可执行,可读,不可写的。数据段的访问控制属性是可 读写。只读数据段(.rdata)中的访问控制属性是只读的,资源文件,动态链接库导入表,常量 一般都存储在该段。如果本进程或者外部进程中的代码尝试写不可读内存,则会触发异常。 常量具体存储在哪个数据段,会因编译器的不同而有差异,但其所在段的访问控制属性应该 是不可写的。该结论的分析过程如下。2.1使用CE查看示例程序中全局变量内存在图-2示例
6、程序运行的日志中可以得知全局常量G_Name的内存地址为0x00452700, 使用CE(Cheat Engine是一款内存分析、修改的工具,和IDA,OD并称为逆向分析的三剑 客)查看到全局变量G_Name所在页的基地址为0x0045200,内区页的访问控制属性是可执 行/只读,故写内存操作会触发异常。那么,全局量G_Name在哪个段(Section)呢?根据其 访问控制属性来看其应该是在代码段,该推论是否正确呢?图-3使用CE查看示例程序内存2.2分析示例程序的PE头在PE文件头中会对程序的段信息详细的描述,如图-4和图-5所示,使用UE打开示 例程序RDataSectionDemo.ex
7、e所呈现PE文件头。关于PE文件结构细节,本文不再展开叙 述,后续如果时间允许,笔者会写一篇专本关于PE文件分析的文章。如图-4所示,在PE文件的IMAGE_NT_HEADER中可以看出示例程序共分为8个段。如 图-5所示可看出这8个段分别为CODE段、DATA段、BSS段、.idata段、.tls段、.rdata 段-reloc段、.rsrc段。每个段头40字节,包括段名,段大小,段RAV地址,段属性信息。 笔者不再对每个段进行一一分析,重点分析代码段(CODE,在其它编译器编译的目标程序中 通常叫.text)。如图-5所示,代码段的RAV地址0x00001000,根据该地址加上exe的基地
8、址 0x00400000(默认是该基地址,在程序编译时可以设置为不同的值)计算出代码段的虚拟基 地址(VA)是0x00401000。代码段的大小(未页对齐)0x00052054,win32默认的内存页 的大小4K,内存页对齐后大小为0x00053000,据此可以推算出代码段的虚拟地址(VA)的范 围是 0x004010000x00454000o同样如图-5所示,可以看出代码段的属性为0x60000020=0x40000000 or 0x20000000 or 0x00000020。其中 0x40000000 表示据段可读,0x20000000 表示段可执行,0x0000002 表示段中包含代码
9、。根据2.1节所分析的全局变量的地址(虚拟地址VA)为0x00452700可以的得出全局常量G_Name是在代码段(CODE段)中的结论,同时根据段属性的分析也佐证了这一点。由于代码段访问控制属性是可执行/只读,所以在修改全局常量的值时会触发异常。OODOOCi5DhOODOOOeDhOQDOQO-3DhOEgCHiQhDQDOQOvDh DggOhOQDOQOOOhOOQCimDhOODOOCl2DhOODQQjbDhoomo-cohODDOQOdDhjijDOGOSDhggg眼ClDDOQClaDhdDDOai&DhOODQQllDh0000013DhODDOOLDhOODOOKDhoom
10、 1$ oh0DDO01DhoODdonchoomieoh0DDOO19DhO-OQiOOlaOhODQGaihDhooDoaiaDhQODOOldDhoomsooh。心OODOQ22Dh 00 0(*0 2 3 Oh aDD0aS4|-|h:ia32.,57IMAGE NT HEADER00ODCODE.IMAGE_NT HEADER 宿移 gdxOOOOOlOO; This psograiL 旬ug;i: De ian under W段数量共8个段00 13心 g DO g ; 一 3 3 - -a a 0CID ;4:我L 北 g 知 ?+?DAT Aii|BB?iiaaB-演:DQQD
11、OuuDOoatrQDDO 000DDGijDDGO ;00GODOgg00gg00GO00DO00-:0000g0-D0。g00QDg0000DO ;ODOD图-4示例程序PE文件DOS和NT_HEADER头&CKCK)210h:gg。二浊;DDaa0230hiDDGu0240h:UUjQJ230h: ODOaijSSOh:: DOOOOSaQh: 000002b0hf OOOQOiScOh!00ll02duh: DO aaa2eoh dd 口加 gzmm k: 0000030011M QO 00 DO Oij DO oa CO g OQ 00 g W g 00 Cl-Ct iCl 心心 心
12、0*50jgcmDOCH:gocoqgg00g000-0-g00000000000004)OD0-0-0。00DOOO00ijijoogqqqqooggOQ00OO口口NCiD口口QDG-O&OD D 5 & o o-J E fl- D & DD D D- 0 & D0 4 0-0000 2 0-000000 0 Q ooaq 0 Q o嚣酬馆曰5区旺HEADERM iCi D & g s 图-5示例程序PE文件段头3. 改变段属性的方法通过第2节的分析,我们已经清楚的知道,为什么在程序运行期间修改常量值会触发异 常的原因。那么有没有方法可以绕过该限制,使得程序运行期间可以修改常量值呢?答案时
13、 肯定的,并且方法有多种,笔者在本文中重点介绍两种方法。3.1静态修改PE文件段属性在第2节的图-5中,我们得知代码段的属性0x60000020=0x40000000 or 0x20000000 or 0x00000020,该值属性值控制了代码段是只读的。那么我们能否通过修改代码段的属性 值赋予其读写属性呢。0x80000000表示段可写,我们将代码段属性修改0xE0000020=0x80000000 or 0x40000000 or 0x20000000 or 0x00000020。如图-6所示,修改示例程序PE文件中数据段属性赋予可写属性,将其另存为新的示例程序(exe)。OOOO-Olf
14、OhGOODOSODhC00Da22DJlQOODD24DhL00j, 2 5JUD00D02tl!DhG000 02331000D02dhDhCOO jo: 2 b jhgg。心 hQ00jC2djiloooimmh0000030011ijOOSjClljh000003部h.id.a.ta7代码段属性修改为 :OnE0OOOD2O 赋予 i可写属性 at上 s9 a I. X P-i reloc.rare图-6 修改代码段属性运行修改过代码段属性的示例程序,不出所料,在程序运行期间可以动态修改常量值了,运行结果如图-7所示。中与.rda财段示ffl好同 |蛔刍有程头陪 |仙 tfi .2CE
15、1-L0-D4 00 KEl-LO-m 00 21321-10-04 CO 2tE!l-LO-01 CO骨或舞髀G Bui t-g; imNHmdds is 4.1UIII*III1+I仁者E明由*部瓦炊1牛圭全方皿*驴2视卫可广左爰厅肴费?漓京而CjQ; 707134332,图-7修改代码段属性后运行结果在现实的生产环境中,通过该方法修改段属性,使常量值在运行期间可以修改有很大的 局限性。为什么这么说呢?因为软件病毒和反病毒,调试于与调试双方一直进行斗志斗勇, 激烈的较量,这也促进了软件技术的发展。在生产环境中软件都会加壳,而且有很多的先进 的壳能到容易加壳但去壳却是不可逆的(关于加壳与脱壳
16、的技术细节笔者在本文中不再展开 叙述)。在实际的生产环境中应用一旦加壳,该方法就很难实施,是否有别的方法可以起到 同样的作用呢?这就是笔者要介绍的第二种方法,在程序运行期间动态修改数据段的属性。3.2动态修改虚拟内存访问控制属性在程序运行期间动态修改常量值的方法有很多种,例如修改CR0寄存器停用内存禁用 内存保护模式,或者通过DML修改虚拟内存的访问控制属性,DML表地址存储在CR3寄存 器。这两种方法都需要有系统内核权限(R0),也就是需要开发驱动程序才能达到该目的。另 外这两种方法很底层,不同的操作系统,32位和64位等相差都比较大,所以笔者不建议使 用该方法。有一种简单易行的方法也可以达
17、到该目的,使用VirtualQuery查询要修改的变量所在的 虚拟内存页,然后通过调用VirtualProtect来修改目标内存页的访问控制属性,这样即可实 现我们的目标。关于这两个函数的细节笔者在此不再赘述,直接贴出相关的代码片段如下图 所示。739 ia n 1215 1 17 ia19 20 21 222 3 24pxccedure Tf rmMain -btnLClick (Sendsr; TOb ject看;:臂辑氐心CEi查询萼串常量所在的虚拟内存页pConst :. Pointer;/rH&m I info : TNemoryBs sicln faimat ion;/Sbegxn
18、/=m G AddrmiBiB; y/G Addrm;B;B 字符串卷ftG Wame由地址ViEtuAlQupryrMQmlnfOy/SiNQOf (TtfemoryBaiS idnfoEMRlsiGn. ;jcRet 7if cRet O SizeOt TKemoryBasicIntcnnstiortJ_t-henbeginwritqLog (1 J4|Virtii*IQuery fc !) r顷箱J修改虚拟内舂页访问控制属性图-8修改虚拟内存页访问属性图-9修改虚拟内存访页问控制属性运行效果4. 关于作者作者长期从事逆向和软件安全方面的研究,长期税控业务领域的工作,成果包含税控 数据采集,开票等。欢迎和广大软件安全技术爱好者及税控业务爱好者进行经验交流,QQ:叫3 2