内容简介:可以看出进行输入判断的关键函数是
ida 打开
gdb 跟一下,知道 0x804a20
是输入(猜也能猜到)
最后答案 L1NUX
WindowKernel
第一次接触这种类型的题目。放在 32 位下来做题。
打开发现报了 error,想了想试试管理员权限打开,可以正常运行了。
点 enable 之后应该是调用了 sys 的函数记录键盘输入,然后进行分析。
查了下程序没有壳,直接放到 ida 里面。这种 gui 的程序,直接看 DialogFunc
函数,过一下发现关键在 sub_401110
函数里面。
可以看出进行输入判断的关键函数是 sub_401280
,这个函数跟进去看果然看到了 DeviceIoControl
函数。正常情况下,应用程序和驱动程序通信的基本过程是,应用程序应用程序使用 CreateFile
函数打开设备,然后使用用 DeviceIoControl
与驱动程序进行通信,包括读写两种操作,结束之后使用 CloseHandle
函数来关闭设备。
接下来看看 sys 文件,拖进 ida 看到函数不多,可以直接读,
然后从 DirverEntry
挨个过一遍的时候看到了比较有意思的函数
这里等于 0x1000 的时候和等于 0x2000 的时候赋值,和之前的 sub_401280
函数一对比,很明显等于 0x1000 的时候开始,然后等于 0x2000 的时候进行判断。所以等于 0x2000 的时候这个 *(_DWORD *)&v3->Type = dword_13024;
赋值就很引人注目了。看看 dword_13024
的引用,跟到了 sub_110D0
,不大看得懂这个函数,没关系继续网上跟.
sub_11266->sub_111DC->sub_11156->sub_110D0
而我们看看 sub_11266
函数,看到了关键的 READ_PORT_UCHAR
函数, READ_PORT_UCHAR
这个 API 是从端口中获取字节信息, 0x60
是 PS2 键盘的数据端口,必须在内核模式下运行。查阅了资料,了解到大多数 PC 键盘控制器在地址 0x60和0x64
上是可寻址的,键盘和鼠标是共享 0X60 端口的,所以在使用之前,系统一般会先读取 0X64
端口,判断是鼠标还是键盘。
回到题目,这里也就清楚了,传入 sub_111DC
的 v4
就是我们的输入,然后就是连等判断,地址 dword_13034
是一个计数器,计数器大于一定值之后又进入 sub_11156
和 sub_110D0
。
最后看下来,我们的输入值如下:
0xa5, 0x92, 0x95, 0xb0, 0xb2^0x12, 0x85^0x12, 0xa3^0x12, 0x86^0x12 0xb4^0x12^0x5, 0x8f^0x12^0x5, 0x8f^0x12^0x5, 0xb2^0x12^0x5
总共 12 个字母,一看不像是可显字符,不过这里从端口读下来的数据可不直接就是可显字符,是键盘扫描码,还得进行一下转换,参照链接 http://www.voidcn.com/article/p-syxmbgac-sm.html
,
转换之后得到 flag: keybdinthook
AutoHotkey1
拿到程序之后,发现加了壳,UPX,懒得用工具,手动简单跟一下,很快就发现入口,
同样用 lordpe 转储一下,然后 importrec 回复 IAT。然后运行,发现报错
程序崩溃,但是这不是系统的错误,程序正常运行然后的弹窗,猜测就是程序进行了自校验,然后脱壳之后无法通过自校验,先放进 ida 里面,然后手动跟一下,迅速定位了校验函数 sub_4508C7
,
结合 IDA 边调边看,主要分为两部分
上述为第一部分,主要是将文件除去倒数四位进行一堆复杂的运算然后和最后四位对比。
上述为第二部分,主要先验证前 16 字节数据为某一固定值,然后第 17 等于 3,18-22 位与 0xFAC1 异或用作下一次读取的长度,然后读出来进入 sub_450ABA
函数进行一大段复杂的运算,然后计算结果似乎只是存入了某段内存,这让人很在意,跟了一下发现结果如下:
很明显的 md5 值,根据 readme 这应该就是其中的一部分了。
解密的结果是isolated
然后后续开始复杂了起来,看得我一头包,然后干脆直接开始找 GetWindowTextA
,但是发现有一堆调用,其中看到一个调用存在 DialogFunc
中,然后确定应该是需要的,地址为 00425FB7
:
下断之后成功截获输入:
接下来应该就是某个地方对其进行了处理,现在 ida 中简单跟了一下,跟的我一头雾水,接下来的几个函数逻辑都比较复杂,看得我一头包,索性不跟了,直接 OD 中对保存我们输入的地方下了个硬件访问断点。然后 F9
之后程序断在了如下所示的一个 cmp
指令,这里将输入和一段疑似 md5 的值进行逐位对比,这一段多半就是答案了。
54593f6b9413fc4ff2b4dec2da337806
解密之后的值是 pawn
在和前一部分组合一下获得 flag: isolated pawn
CSHOP
查了下壳发现是.NET 程序,用 .net reflector
打开看看,主要是调用这个框架
看看 InitializeComponent
函数,新建了 1 个按钮和 10 个 label,其中按钮的大小被设置为 0 了,然后按钮一旦被按下就会触发 click
函数,该函数就是对标签进行赋值,猜测就是 flag,但是需要知道标签的排列顺序,
这个时候我们可以回到 InitializeComponent
函数,看到每个标签都有一个 location 属性,通过这个属性我们可以把标签排列,然后根据 click 里面的赋值就能获取 flag。
但是还有一个问题,下图两个标签在反编译看到的都是 lbl\n
,即是一样的无法区分,这两个标签分别决定了按钮按下生成值的前两位。而后八位可以分析出来是 W6RP6SES
而 click 函数中对 lbl\n
赋值总共有三个分别是 P,4,5
,那可能性就不多,总共 6 种
P4W6RP6SES P5W6RP6SES 4PW6RP6SES 5PW6RP6SES 45W6RP6SES 54W6RP6SES
尝试之后得到答案 P4W6RP6SES
。
那做完之后看别人的题解得到了几种新的解题方法。
法一
直接摁回车,在对一些程序我们摁 TAB 键的时候,它会切换按钮或者标签,这个 TabIndex
属性就意味着 TAB 键的切换顺序,而按钮的顺序是 0 即默认选中,所以打开程序直接摁回车就出 flag 了。。。。。
法二
尝试将原来 size 为 0 的按钮调出来。
利用 spy++
定位到 button 的窗口句柄:
然后 52 破解 工具 包中有个窗口信息查看器 spy
,找到这个 button,之前说过了,button 的 size 为 0,所以没法儿点击,但是通过这个工具我们可以将按钮最大化:
点后面的勾,然后发现程序窗口中的按钮最大化了,直接摁点一下,再把这个 button 最小化就能看到 flag。
法三
我自己做的时候由于 reflector 可能版本比较老?导致两个变量无法区分,后来看了别人用 dnspy
,试了试,如下图:
直接就能看出来,那就没啥说的了,直接就可以怼出 flag 了。
ok 换工具了。。。。。。
PEPassword
题目给了两个代码,一个 origin.exe
,一个 packed.exe
,简单分析就知道 origin.exe 是程序的原始程序,packed.exe 是程序加密之后的, origin.exe
运行效果如下:
分析一下之后确实没有 password,那么猜想是 packed.exe
里面会有,但是要运行 packed.exe
需要输入一个密码,所以工作就来了,找到破解加密,然后运行程序应该就会有下一步了。
那么来看看这个 packed.exe
,放到 IDE 里面,没啥信息,OD 跟一下之后发现一直在这里循环,
ctrl+F8
跑起来,然后看到最后那个 cmp,数据窗口跟随发现我们输入就在后面,纯属偶然发现。那就在我们的输入下一个硬件访问的断点:
断下来之后发现调用源自 0x409190
:
那这里其实正常可以对 WM_KEYDOWN
消息下消息断点。
然后往下走,进入了一串复杂的计算函数 0x4091d8
,将我们的输入进行了一串及其复杂的运算之后得到四字节的数据与 0xe98f842a
对比,分析了一下这个函数之后实际上是一个 0x10000*输入长度的大循环
,具体不多说了,那这个代码也不是很想看,所以直接绕过 cmp,阻止 0x4091a6
处的跳转,然后继续往下跟,之后代码运行到了 0x409200
,来到了关键的加密函数。
首先这一段选中代码计算了一个 ebx
和一个 eax
,用于后面的解密运算:
edi
处存储加密的代码,然后 edx 赋初值之后进入解密循环,解密之后运行,如果能够解密成功就能成功运行。解密循环如下
很简单的一段迭代算法,我们根据 origin.exe
和 Packed.exe
两个程序可以很容易异或出每个阶段的 eax,然后需要爆破一下 ebx,总共爆破空间为 0xffffffff
,还可以。
那刚开始用 python 写的,如下:
from pwn import u32,p32 import threading ori=['\x81\xec\x4c\x01','\x00\x00\x56\x57'] en=['\x17\x2e\xe6\xb6','\x05\x7e\x0c\x0d'] eax=u32(ori[0])^u32(en[0]) eax2=u32(ori[1])^u32(en[1]) print 'eax:',hex(eax) print 'eax2:',hex(eax2) def ro(lst, k,flag): if flag==-1: k=k%32 k=-k else: k=k%32 x = lst[:k] x.reverse() y = lst[k:] y.reverse() r = x+y return list(reversed(r)) def go(start,end): for i in xrange(start,end): ebx=i ebx_tmp=int("".join(i for i in ro(list("{:0>32}".format(bin(ebx)[2:])),ord(p32(eax)[0])%32,1)),2) eax2_tmp=ebx_tmp ^ eax if eax2==int("".join(i for i in ro(list("{:0>32}".format(bin(eax2_tmp)[2:])),ord(p32(ebx_tmp)[1])%32,-1)),2) : print "ebx:",ebx if __name__ == "__main__": t_pool=[] for j in xrange(0x10-1): t=threading.Thread(target=go,args=(j*0xfffffff,(j+1)*0xfffffff)) t_pool.append(t) for j in t_pool: j.start() while 1: pass
跑了好一会儿才意识到,emmm 有点慢,换 c 之后可以使用内联汇编,这样就不用重写代码了,如下:
// vs2012 #include "stdafx.h" int cal(int eax_1,int ebx_1){ unsigned int result; __asm{ mov eax,eax_1; mov ebx,ebx_1; mov cl,al; rol ebx,cl; xor eax,ebx; mov cl,bh; ror eax,cl; add ebx,eax; mov result,eax; } return result; } int main(){ unsigned int eax=0xb7aac296; unsigned int ebx; unsigned int eax2=0x5a5a7e05; for(unsigned int i=0;i<0xffffffff;i++){ ebx=i; if(eax2==cal(eax,ebx)){ printf("success: %x\n",ebx); } if(i%0xf00000==0){ printf("%x\n",i); } } }
运行结果如下:
跑出了两个答案,试一下就知道正确值是 0xc263a2cb
,然后在进解密循环的时候修改 eax 和 ebx,从而使程序正常解密并运行:
得到 flag: From_GHL2_!!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Android编程权威指南
[美] Bill Phillips、[美] Brian Hardy / 王明发 / 人民邮电出版社 / 2014-4 / CNY 99.00元
权威、全面、实用、易懂,是本书最大的特色。本书根据美国大名鼎鼎的Big Nerd Ranch训练营的Android培训讲义编写而成,已经为微软、谷歌、Facebook等行业巨头培养了众多专业人才。作者巧妙地把Android开发所需的庞杂知识、行业实践、编程规范等融入一本书中,通过精心编排的应用示例、循序渐进的内容组织,以及循循善诱的语言,深入地讲解了Android开发的方方面面。如果学完一章之后仍......一起来看看 《Android编程权威指南》 这本书的介绍吧!