wu0o0pj 发表于 2020-4-28 13:13

ASLR影响下修复一dll的某个bug

00 前言:
      之前在使用某软件的报表组件时发现它导出文件时无法写入中文,最近闲了下来,尝试修复其bug;
      本文涉及 OD 的导入库、用 LordPE 关闭 ASLR、添加 IAT、修补代码时对 ASLR 的处理等内容;
      在叙述时,会把一些思路进行说明,所以有些啰嗦,请多多包涵,也希望给萌新们带来收获;

01 定位问题点:
      测试用例就不细说了,就是简单的从数据库中查询记录显示到报表组件中,添加一按钮,实现报表导出为 csv 文件;
      首先载入OD,F9 运行,因为是导出文件,所以下 WriteFile 断点
      

      主程序界面上点击按钮,进行文件导出,此时 OD 停在 WriteFile 断点处,ALT+F9,返回到用户领空
            

      此时发现用户领空中有一些 API 只有序号,没有函数名,这样很难分析,那首先要显示出函数名。
      像图中使用的是 mfc100u.dll,那就需要 mfc100u.lib 文件,因为电脑里只安装了 VS2017,没有 2010,找不到该文件,然而翻遍了全网都没有找到直接下载的;
      最后,还是在本论坛悬赏帖里找到了 冰若° 大佬 OD-2.0 ,里面有相当全的 lib 库文件,感谢 冰若° 大佬!(下载帖 https://www.52pojie.cn/thread-704349-1-1.html,下载后有报毒,没有分析过,冰若°大佬有篇帖子是说,程序加过 UPX 壳,杀软会误报;咱小白,啥都不懂,幸好是只需要里面的导入库 ^_^)

      将导入库文件夹下的所有文件复制到 OD 目录下的 lib 文件夹下,而后 OD菜单栏“调试” - “选择导入库”,弹出框中点击“添加”,选择 OD/lib 目录下的所有文件(是的,可以多选,那直接 Ctrl+A,方便),点击“处理” 即可。
      
      
      重新载入程序,按之前的操作,断下后回到用户领空,此时 API 已经能看到函数名称了。
      
      向上翻,可以看到程序写入文件是用的 CStdioFile的WriteString,bd下该函数导出中文时需要 setlocale( LC_CTYPE, "chs" )。

      Ctrl+N,查看该 dll 导入表,发现没有 setlocale,因此需要添加导入表。
      

      而修补的时机选择在此处 的 push 0,因为该处刚好是下方 CStdioFile::Open 需要的三个参数,不会引起异常,再往下是循环写入文件,不适合;
      另外因为是 __stdcall 的关系,之后修补代码时不需要关注寄存器的值,只需注意堆栈就行了。
      

      整理下思路,要做的事情是:
      1. 添加导入API setlocale;
      2. 在 push 0 的地方 jmp 到 修补代码处;
      3. 修补 setlocale( LC_CTYPE, "chs" );
      4. 补全 push 0 之后被占用的原代码,jmp 回原代码;

02 修补
0201 去掉 ASLR
      为了方便,先关闭 ASLR,去掉重定位带来的不便。。
      如图,运行 LordPE,点击“PE编辑器”,选择 dll,点击“特征值”,勾选“重定位已分离”,“确定”,“保存”,“确定”,“退出”。
      

0201 添加 API
      打开 StudyPE+,将 dll 拖入 StudyPE+,点击“导入”,右键,“添加导入函数”;选择文件 mvcr100.dll,选择 API setlocale,点击“添加”;记录下该 API 的 RVA,也就是 0x00055CDC。
               

0203 补代码
      OD 载入 DLL,因为只有一小段新增代码,所以没有新增区段,直接在最后找一段全 0 的地方,用来补代码。
      首先是字符串 "chs",再往下另找一段,补 setlocale,以及原代码处 jmp 下来
            

0204 测试
      这样修改真的就好了吗?实践进行检验。
      将修改的保存下来,替换原文件,测试后发现程序挂了...
      什么原因呢?还是 ASLR 的问题,虽然 dll 关了,但 exe 没关,所以在使用字符串或是 API 时 还是受 ASLR 影响,会出问题。
      

03 处理ASLR
      怎么处理 ASLR?
      1. 啥都不管,把 exe 的 ASLR 关了;(嗯,也行,但有点暴力,实在没办法了再这么干)
      2. dll 处理重定位表;(没有找到好用、能增、能改的 重定位处理工具,手动处理容易出错,主要是需要对PE有较深的理解,含泪舍去)
      3. 绕过重定位,使修补的代码不受 ASLR 影响;

      选择方案3,绕过重定位。
      分析上图出错位置,发现在 push 字符串 和 调用外部 API 时,会受ASLR影响,而 jmp 下来和 jmp 回去都没事,差别是什么呢?
      文件内部 call 或者 jmp 时,虽然 汇编代码看上去是 jmp XXXXXXX,但其实它二进制代码用的是相对地址,所以无论基址是多少,都没关系。
      因此,需要把字符串和API的地址通过相对地址进行计算再获取,此时需要获取 EIP 的值。

      EIP 的值是不能直接得到的,即 不能 mov eax, eip。
      了解 call 和 ret 本质的同学就清楚了,在 call 时,会把下一句的指令地址入栈,此时只要 pop eax,那 eax 就能得到 eip 了。
      把之前的代码进行修改,为了区分,我另开了一段,进行修补,再把原代码 jmp 到新的位置,修补代码如下:
      
      保存修改,替换原dll,再次测试,功能OK;开启 dll 的 重定位,再测,OK;放到其他电脑中测试,功能也OK。
      终于成功了,很有成就感!{:301_1000:}
      
04 结尾
      篇幅略长,谢谢观看。
      如有错误,请指出,我会及时修改,防止误认。
      如有好的编辑重定位表的工具,请推荐。


wu0o0pj 发表于 2020-4-30 09:16

gh0st_ 发表于 2020-4-29 14:15
修复重定位的工具还是有很多的

求大佬分享{:301_1003:}

之前搜了一些修复重定位表的工具,大多都是用在脱壳后的修复,原理好像是比对脱壳前后的重定位表;
而我这是本身不是脱壳,而是在原有基础上新导入了一个API,新加了个字符串,那需要在原有重定位表上增加几条规则;

论坛里有找到大佬分享的一个工具,https://www.52pojie.cn/thread-352725-1-1.html,但没有使用说明,我添加了地址后,用 LordPE 查看重定位表,发现新增的记录是 error,实际运行也是直接挂掉

因为找不到工具,之后才想到的用相对地址做偏移,绕过了重定位

pojie6 发表于 2020-4-28 19:28

总结的不错!偶尔我也干这事。看了文章,大意是原程序导出中文错误,为了正确导出中文,需要修补原程序增加执行setlocale( LC_CTYPE, "chs" ),再导出就OK了。这篇文章就是介绍 如何修改原程序加入汇编码的问题!

redapple2015 发表于 2020-4-28 13:31

楼主的基础功底真的厉害。学习了

bester 发表于 2020-4-28 13:59

看完感觉楼主的思路很不错,居然利用了eip做偏移计算,

爱飞的猫 发表于 2020-4-28 15:49

把软件发出来呀。。

LPP25621832 发表于 2020-4-28 22:37

caoxin 发表于 2020-4-29 10:19

学习技术来了

wu0o0pj 发表于 2020-4-29 13:03

jixun66 发表于 2020-4-28 15:49
把软件发出来呀。。

软件就不发了,是某个商业软件

这里主要分享下思路{:301_1001:}

wu0o0pj 发表于 2020-4-29 13:04

pojie6 发表于 2020-4-28 19:28
总结的不错!偶尔我也干这事。看了文章,大意是原程序导出中文错误,为了正确导出中文,需要修补原程序增加 ...

对头,就是酱紫{:301_987:}

gh0st_ 发表于 2020-4-29 14:15

修复重定位的工具还是有很多的
页: [1] 2
查看完整版本: ASLR影响下修复一dll的某个bug