Sec Hotspot 首页  排行榜  收藏本站  技术博客  RSS
统计信息
已收录文章数量:11906 篇
已收录公众号数量:89 个
本站文章为爬虫采集,如有侵权请告知
已收录微信公众号
网信中国 区块链大本营 白说区块链 区块链投资家 区块链官微 区块链铅笔Blockchain HACK学习呀 二道情报贩子 合天智汇 小白帽学习之路 小米安全中心 弥天安全实验室 SAINTSEC SecPulse安全脉搏 TideSec安全团队 360安全卫士 游侠安全网 计算机与网络安全 安全祖师爷 安全学习那些事 腾讯安全联合实验室 黑客技术与网络安全 安全圈 腾讯御见威胁情报中心 Python开发者 Python之禅 编程派 Python那些事 Python程序员 安全威胁情报 吾爱破解论坛 行长叠报 安在 i春秋 嘶吼专业版 E安全 MottoIN 网信防务 网安杂谈 数说安全 互联网安全内参 漏洞战争 安全分析与研究 邑安全 ChaMd5安全团队 天融信阿尔法实验室 安全牛 SecWiki 安全学术圈 信安之路 漏洞感知 浅黑科技 Secquan圈子社区 奇安信集团 奇安信 CERT 国舜股份 雷神众测 盘古实验室 美团安全应急响应中心 瓜子安全应急响应中心 顺丰安全应急响应中心 蚂蚁金服安全响应中心 携程安全应急响应中心 滴滴安全应急响应中心 字节跳动安全中心 百度安全应急响应中心 腾讯安全应急响应中心 网易安全应急响应中心 OPPO安全应急响应中心 京东安全应急响应中心 Bypass CNNVD安全动态 安恒应急响应中心 天融信每日安全简报 奇安信威胁情报中心 看雪学院 黑白之道 水滴安全实验室 安全客 木星安全实验室 云鼎实验室 绿盟科技安全预警 白帽汇 深信服千里目安全实验室 腾讯玄武实验室 长亭安全课堂 FreeBuf 绿盟科技 nmask
从一道CTF题目中学习反调试
本文来自公众号:安全客   2020.06.29 18:00:44





前言
上周五空指针的 re第二次公开赛
本身是初学逆向,当时一直卡在反调试(太菜了..),smc一直未能解密得到正确代码,赛后找apeng大佬看wp又仔细琢磨了一下,有些收获,便写文章做个记录。

题目分析
首先运行一下程序
然后动态调试会发现代码出现异常,异常处理程序输出”not welcome!”,所以可以断定程序存在反调试。
拖进ida32打开,f5后分析main函数。
argc 是命令行总的参数个数,包含路径
argv[]包含argc个参数, 第0个参数是路径
这里首先执行sub_402DC0函数进行smc修改loc_402B50处的代码,然后将argv[1]作为参数传递给函数sub_402B50,当然此时静态分析看到的loc_402B50处的数据还是一片混乱。
我们查看sub_402DC0函数,可以明显看到是一个smc,起始地址为loc_402B50,要改变的内存区域大小为0xBC。
再跟进sub_4032A0函数,发现是以byte_452F90为key,然后rc4解密sub_402b50的代码。所以我们的关键应该分析byte_452F90里数据的变化,得到正确的byte_452F90数据即可进行正确的smc解密。

然后我们查看byte_452F90的交叉引用,发现除了执行smc的函数以外,只有TopLevelExceptionFilter函数用到,而这个函数是一个顶层异常处理函数,暂时可以忽略。
但是我们看到byte_452F90上面还有一个数组,猜想可能是修改byte_452F80时修改了byte_452F90处的值。
我们查看交叉引用发现有6处修改吗,但是修改byte_452F80的代码都没有被识别为函数。
我们一一进行分析,按p定义函数(这里ida未识别的原因可能是这里的函数没有被交叉引用,我调试发现是在cinit处调用的),然后重命名,接下来我们依次分析这四个反调试。

反调试分析

debuging1

这里调用Ntdll.dll的NtQueryInformationProcess函数,它用来提取一个给定进程的信息。
它的第一个参数是进程句柄,第二个参数告诉我们它需要提取进程信息的类型。为第二个参数指定特定值并调用该函数,相关信息就会设置到第三个参数。第二个参数是一个枚举类型,其中与反调试有关的成员有ProcessDebugPort(0x7)、ProcessDebugObjectHandle(0x1E)和ProcessDebugFlags(0x1F)。例如将该参数置为ProcessDebugPort,如果进程正在被调试,则返回调试端口,否则返回0。
这里第二个参数v3为ProcessDebugObjectHandle(0x1E),如果进程正在被调试,DebugObjectHandle会设置到第三个参数v4(也就是v75)。
然后紧接着的是一个条件判断,正确的执行流程v75应该为0,也就是执行else语句的内容,所以我们把这里的jnz loc_401FE9 给nop掉即可。
patch后执行的是这一块代码,可以看到修改了byte_452F80为起始地址的32个字节,也就是确实有修改到我们前面说的byte_452F90(lc4的key),剩余的3个反调试函数也是一样对byte_452F90做了修改。

debuging2

这里检查进程环境块( PEB )中的调试标志。
Windows操作系统维护着每个正在运行的进程的PEB结构,它包含与这个进程相关的所有用户态参数。这些参数包括进程环境数据,环境数据包括环境变量、加载的模块列表、内存地址,以及调试器状态。
typedef struct _PEB {  BYTE                          Reserved1[2];  BYTE                          BeingDebugged; //被调试状态  BYTE                          Reserved2[1];  PVOID                         Reserved3[2];  PPEB_LDR_DATA                 Ldr;  PRTL_USER_PROCESS_PARAMETERS  ProcessParameters;  BYTE                          Reserved4[104];  PVOID                         Reserved5[52];  PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;  BYTE                          Reserved6[128];  PVOID                         Reserved7[1];  ULONG                         SessionId;} PEB, *PPEB;
进程运行时,位置fs:[30h]指向PEB的基地址,所以这里的v46就是检查BeingDebugged标志,我们要做的就是patch使得v46的值为0即可。
然后还可以看到byte_452F80的赋值代码和v37和v45有关,这两个变量的值为GetTickCount()函数的返回值,GetTickCount函数返回最近系统重启时间与当前时间的相差毫秒数,是一个时钟检测。
emm但是好像只要不在两个函数中间下断点应该不会有问题,v37-v45正常情况会是一个很小的值(我自己调试时是0),然后右移两位后应该为0,数据与0异或不发生任何改变。

debuging3

这里是对关键位置的代码求和
v35的值会被拿去异或
v37的值是一个基于rdtsc指令指令的时间检测,正常执行应该为0
v36的值是一个基于GetTickCount()函数的时间检测,正常执行应该为0
v30到v34是5处关键位置的代码求和,这涉及到调试器的软件断点原理,简单来说也就是会用int 3指令替换原代码,所以我们不能在这些代码处下断点
然后还有一项关键数据就是
(*(_DWORD *)(*(_DWORD *)(__readfsdword(0x2Cu) + 4 * TlsIndex) + 4) & 0xFF)
这里是另一处反调试的点,在TLS回调函数中,后面会讲,先说结果,这一项的值应该是0
v35由这么多项求和得到,调试得到,v35的值应该为0x21A,所以我们要做的将v35的值patch为0x21A

debuging4

这里是检查进程环境块( PEB )中的NTGlobalFlag
由于调试器中启动进程与正常模式下启动进程有些不同,所以它们创建内存堆的方式也不同。系统使用PEB结构偏移量0x68处的一个未公开位置,来决定如何创建堆结构。如果这个位置的值为0x70,我们就知道进程正运行在调试器中。
和debuging2类似,我们只需要patch使得v24的值为0即可。

TLS回调函数

上面debuging3中遇到的
*(_DWORD *)(*(_DWORD *)(__readfsdword(0x2Cu) + 4 * TlsIndex) + 4)
其实是在tls回调函数中被赋值的,这里的赋值操作同样是一个反调试
这个函数可以在导出表中看到,或者用xdbg调试也会断在tls回调函数
而ida的函数表里没有出现这个函数是因为有花指令阻碍了ida的分析
自行nop修改后按p重新定义函数即可看到函数反编译内容
我们可以看到这里的反调试和debuging1中的反调试是基本一样的,正确的执行流程result的值应该为0,v8的值也应该为0,所以我们把这里的
.text:00401D98                 jz      short loc_401DB5
jz指令patch为jump指令即可

总结
至此,此题的反调试点已经全部干掉,保存到原文件后就可以随意调试这个这个程序了
完成了smc后程序就进入了正常加密过程
首先sub_402B50对我们输入的key进行了移位
然后触发异常,第一个异常处理函数是一个较复杂的异或操作,第二个异常处理函数是魔改的sm4加密算法,最后会将加密的数据逐个进行比较,正确则输出”right!flag is npointer{your_key}”
详细的解密过程可以去看apeng大佬的博客,或者官方公众号的wp。

参考链接
apeng大佬的博客: 2020 空指针 5月RE公开赛




- End -


精彩推荐

从0到1学会搭建小型企业拓扑到由外向内的渗透测试

BlueLeaks:24年的警察机密数据被泄露

“裸聊APP”背后的秘密

小心!房间里的灯泡可能泄露你说过的话



觉得内容不错就点个 “在看” 吧!