0x00背景
昨天遇到了一个很有意思的CrackMe小demo,其实想要破解它还是很简单的,但是想要弄清楚它的机制,对于我这个新手来说还是有点麻烦的,为了弄清它的原理,故记录一下探索过程。
0x01 分析过程
看起来就是个破解密码的小demo,也没啥玄机,用resource hacker看看有没有什么隐藏的组件之类的。
这个应该是密码输入正确后显示的图片,也就是没有我们想要的MessageBox,不能直接找上面的过程分析了。
看起来资源也没有多少,看看组件的ID就行,在后面可能会用到,然后我们打开OD,找找在哪里用到了这个组件ID,首先猜想一下至少会在获取组件的输入的时候用到,也就是GetDlgItemTextA/W这里。
找到了,接下来我们在这儿下个断点,看看数据是怎么走向的。
接下来单步步过一下:
可以看到,EIP就突变了,一下子不知道跑哪儿去了,我到这里的时候有点懵,看来直接上OD不太行,然后我就打开IDA静态分析一下这个程序的结构是怎样的。
我们一下子就能定位到GetDlgItemTextA这个函数,双击来到代码段,然后Ctrl+x看看交叉引用,很好,逻辑块尽收眼底,接下来我们看看有什么奥秘:
先看看调用函数的地方干了什么,毕竟它要验证我们输入的密码对不对,就必然会有字符串的比较,C/C++有strcmp,但是这没有,所以应该是汇编语言最原始的ESI/EDI的字符一个个比较。
我们 按下空格,看看代码:
发现了一个异常,我们再次按空格回到逻辑图,看看有没有处理异常的地方:
这里有一个异常处理函数,而且是UEH
,只要产生了异常,并且没有处理函数能处理,都会调用这个函数指定的回调函数去处理,也就是说这个函数的参数时真正处理异常的函数的地址。也就是loc_40334A
,我们跟进到这个函数看看这里是啥,怎么处理的异常数据。
SetUnhandledExceptionFilter function (errhandlingapi.h) - Win32 apps |微软文档 (microsoft.com);k(SetUnhandledExceptionFilter);k(DevLang-C%2B%2B);k(TargetOS-Windows)%26rd%3Dtrue)
可是当我们来到这里的时候却一下子懵了,这都是什么鬼啊,往左边一看,发现是.data
也就是数据段,这些所谓的db就是它的二进制代码,还好IDA智能,我们可以选中这块区域,按c和p键将它转化成汇编代码:
看吧,这就好分析多了:
0x011 误入歧途
然后我就跑到OD去找这个函数了,毕竟这样看也能什么线索。
一步步单步走下来发现了这些东西,这里[EBP + 0x8]
是进入此函数的第一个参数
到这里,就又不得不提一下UEH指定的顶级异常处理函数了,这里是它的函数原型,参数是
它的参数是一个指向异常信息的结构体指针,也就是这样的:
EXCEPTION_POINTERS (winnt.h) - Win32 应用|微软文档 (microsoft.com);k(_EXCEPTION_POINTERS);k(DevLang-C%2B%2B);k(TargetOS-Windows)%26rd%3Dtrue)
然后它的第一个字段是
再看看这个结构体,它的第一个字段又是异常码:
然后结合OD步过到这里的EAX看这个错误码刚好是0xC0000005
,刚好就是内存访问异常:
所以到这里就很顺了,自己找的没错,也就是我们无论输入什么,异常处理函数的前几句话都是把0xC0000005
和0x00DEADFF
按位与一下然后左移5位得到0xA0
,继续往下走就出现了字符串magic,把这个字符串保存在某个地方,然后结合这张图就会认为之前之前跟fubar比较后来变成了跟magic比较,关键是此时magic确实就是正确的答案。可是为什么呢?我们在异常处理函数内部并没有发现EDI的变化呀。
今天来探究一下,到底在哪里神不知鬼不觉改了EDI的值,导致我们不跟“fubar”比较了,变成了跟“magic”比较。
0x012 步入正轨
前景回顾一下,刚刚进入这个终极异常处理函数的时候,ESI
是第一个参数的解引用,也就是ExceptionRecord
字段,经过LODSD
操作后把ESI
的数据转移到EAX
中了,然后解了一次引用,此时的EAX保存的是异常码,然后EAX经过按位与和左移操作之后变成了0xA0
,关键就是这里的LEA指令的这一块地方,还有上面的LODSD指令:
上面不是把ESI的值转移到EAX中了吗,有一个细节就是ESI此时已经不是ExceptionRecord
的地址了,因为它已经发生了这个结构体大小的偏移,也就是说它现在是ExceptionPointers的第二个字段:PCONTEXT
,它是另一个结构体的指针:
紧接着ESI
就解了一次引用,来到了Context
结构体,有好多字段,看起来都是一些寄存器之类的:
CONTEXT (winnt.h) - Win32 应用|微软文档 (microsoft.com)
然后这个地址加了一个0x9C:
来看看是啥被改了?可不就是它么?
原来一直没有被发现的EDI突变发生在了这里,之前计算得到的EAX的值被赋值给了EDI,然后出了这个函数就被还原到了各个寄存器,我们看一下之前计算得到的地址是不是“magic”字符串的地址:
0x02 小结
千万不能“想当然”,不能放过一丝细节,不要我们觉得合理了就不管了,当然,最重要的还是基础要牢固,这次的坑就挖在了LODSx
指令上了,完全没有在意它的涵义。
至此程序分析完了,分享一下思路,同时也是教训。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)