且行且远

Archive for the ‘逆向手札’ Category

分类: 逆向手札 由 ssfighter 于 2006年1月3日 发表

先说一下,这个crackme已经很老了,也有无数人分析过这个简单的crackme了,我这个大菜鸟昨天晚上尝试着自己跟了一下,有点小收获,写出来,希望高手看不到我这个帖子,否则肯定要笑掉大牙了:)

crackme文件的下载地址是:http://bbs.pediy.com/upload/files/1084801702.zip
riijj写的的一个很清楚的破文在这里:http://bbs.pediy.com/showthread.php?s=&threadid=7505&perpage=15&pagenumber=1

首先侦壳,用PeiD0.94打开,发现显示为Microsoft Visual C++ 6.0,基本判断为无壳
再用Ollydbg打开,运行之后,选择搜索-所有参考的文本字符串,找到“Registration Fail”的地址,来到这里:

00401058 /75 74 jnz short ncrackme.004010CE
0040105A |8B4424 0C mov eax,dword ptr ss:[esp+C]
0040105E |66:3D EA03 cmp ax,3EA
00401062 |75 42 jnz short ncrackme.004010A6
00401064 |E8 C7010000 call ncrackme.00401230 ;

这个应该是计算的位置,在这里设断,输入用户名ssfighter,密码abcd1234。跟进去看看

00401069 |85C0 test eax,eax ; 比较
0040106B |6A 00 push 0
0040106D |68 80504000 push ncrackme.00405080 ; ASCII “ncrackme”
00401072 |75 1B jnz short ncrackme.0040108F ; 关键跳
00401074 |A1 B8564000 mov eax,dword ptr ds:[4056B8]
00401079 |68 64504000 push ncrackme.00405064 ; ASCII “Registration successful.”
0040107E |50 push eax
0040107F |FF15 C0404000 call dword ptr ds:[<&USER32.Message>; USER32.MessageBoxA
00401085 |E8 A6020000 call ncrackme.00401330
0040108A |33C0 xor eax,eax
0040108C |C2 1000 retn 10
0040108F |8B0D B8564000 mov ecx,dword ptr ds:[4056B8]
00401095 |68 50504000 push ncrackme.00405050 ; ASCII “Registration fail.”
0040109A |51 push ecx
0040109B |FF15 C0404000 call dword ptr ds:[<&USER32.Message>; USER32.MessageBoxA
004010A1 |33C0 xor eax,eax
004010A3 |C2 1000 retn 10

跟进call之后来到这里:
00401230 8B0D BC564000 mov ecx,dword ptr ds:[4056BC]
00401236 83EC 30 sub esp,30
00401239 8D4424 00 lea eax,dword ptr ss:[esp]
0040123D 53 push ebx
0040123E 56 push esi
0040123F 8B35 94404000 mov esi,dword ptr ds:[<&USER32.GetD>; USER32.GetDlgItemTextA
00401245 6A 10 push 10
00401247 50 push eax
00401248 68 E8030000 push 3E8
0040124D 51 push ecx
0040124E 33DB xor ebx,ebx
00401250 FFD6 call esi
00401252 83F8 03 cmp eax,3 ; 用户名>3位
00401255 73 0B jnb short ncrackme.00401262
00401257 5E pop esi
00401258 B8 01000000 mov eax,1
0040125D 5B pop ebx
0040125E 83C4 30 add esp,30
00401261 C3 retn
00401262 A1 BC564000 mov eax,dword ptr ds:[4056BC]
00401267 8D5424 28 lea edx,dword ptr ss:[esp+28]
0040126B 6A 10 push 10
0040126D 52 push edx
0040126E 68 E9030000 push 3E9
00401273 50 push eax
00401274 FFD6 call esi
00401276 0FBE4424 08 movsx eax,byte ptr ss:[esp+8] ; 取用户名第一位
0040127B 0FBE4C24 09 movsx ecx,byte ptr ss:[esp+9] ; 取用户名第二位
00401280 99 cdq ; edx与eax组合
00401281 F7F9 idiv ecx ; eax除以ecx,商为eax
00401283 8BCA mov ecx,edx ; ecx为余数
00401285 83C8 FF or eax,FFFFFFFF ; eax=FFFFFFFF
00401288 0FBE5424 0A movsx edx,byte ptr ss:[esp+A] ; 取用户名第三位到edx
0040128D 0FAFCA imul ecx,edx ; 将刚才的余数与第三位相乘
00401290 41 inc ecx ; ecx加一
00401291 33D2 xor edx,edx ; edx置零
00401293 F7F1 div ecx ; eax/ecx商放到eax
00401295 50 push eax ; eax压栈
00401296 E8 A5000000 call ncrackme.00401340 ; 取刚才的结果放到内存中

又有一个call,跟进之后发现是如下程序段:
00401340 8B4424 04 mov eax,dword ptr ss:[esp+4]
00401344 A3 AC504000 mov dword ptr ds:[4050AC],eax
00401349 C3 retn

看来是把刚才计算的结果写到了4050AC内存中,记住这个地方,后面的计算都是在反复读取这里。

继续运行:
0040129B 83C4 04 add esp,4 ; 堆栈平衡
0040129E 33F6 xor esi,esi ; esi置零
004012A0 E8 A5000000 call ncrackme.0040134A
这个call里面有一大堆复杂的计算,先放着。
004012A5 99 cdq ; edx置零
004012A6 B9 1A000000 mov ecx,1A
004012AB F7F9 idiv ecx ; 刚才计算结果除以26
004012AD 80C2 41 add dl,41 ; 余数再加上65
004012B0 885434 18 mov byte ptr ss:[esp+esi+18],dl ; 算得的结果中一位写入堆栈
004012B4 46 inc esi
004012B5 83FE 0F cmp esi,0F ; 一共15位
004012B8 ^ 72 E6 jb short ncrackme.004012A0
004012BA 57 push edi ; 至此计算完毕

这时发现堆栈中写入了15个奇怪的大写字母:
0012FAE8 4E55564A JVUN
0012FAEC 42425345 ESBB
0012FAF0 43564F45 EOVC
0012FAF4 00525454 TTR.

可是这并不是注册码,继续看下面的程序:
004012BA 57 push edi ; 至此注册码计算完毕
004012BB 8D7C24 0C lea edi,dword ptr ss:[esp+C]
004012BF 83C9 FF or ecx,FFFFFFFF
004012C2 33C0 xor eax,eax
004012C4 33F6 xor esi,esi
004012C6 F2:AE repne scas byte ptr es:[edi]
004012C8 F7D1 not ecx
004012CA 49 dec ecx ; 得到用户名位数
004012CB 74 59 je short ncrackme.00401326
004012CD 8A4434 0C mov al,byte ptr ss:[esp+esi+C] ; 取用户名的一位
004012D1 C0F8 05 sar al,5 ; 右移5位
004012D4 0FBEC0 movsx eax,al
004012D7 8D1480 lea edx,dword ptr ds:[eax+eax*4]
004012DA 8D04D0 lea eax,dword ptr ds:[eax+edx*8]
004012DD 8D0440 lea eax,dword ptr ds:[eax+eax*2] ; eax=123乘以右移之后的数
004012E0 85C0 test eax,eax
004012E2 7E 0A jle short ncrackme.004012EE
004012E4 8BF8 mov edi,eax
004012E6 E8 5F000000 call ncrackme.0040134A ; 又看到了这个call
004012EB 4F dec edi
004012EC ^ 75 F8 jnz short ncrackme.004012E6 ; 这里是一个循环
004012EE E8 57000000 call ncrackme.0040134A ; 又是这个call,这应该是最后一次计算

004012F3 99 cdq ; edx置零
004012F4 B9 1A000000 mov ecx,1A
004012F9 8D7C24 0C lea edi,dword ptr ss:[esp+C]
004012FD F7F9 idiv ecx
004012FF 0FBE4C34 2C movsx ecx,byte ptr ss:[esp+esi+2C] ; 取填入的注册码
00401304 80C2 41 add dl,41 ; 余数加65
00401307 0FBEC2 movsx eax,dl
0040130A 2BC1 sub eax,ecx
0040130C 885434 1C mov byte ptr ss:[esp+esi+1C],dl
00401310 99 cdq
00401311 33C2 xor eax,edx
00401313 83C9 FF or ecx,FFFFFFFF
00401316 2BC2 sub eax,edx
00401318 03D8 add ebx,eax ; 比较注册码一位,相同则ebx为0
0040131A 33C0 xor eax,eax ; eax=0
0040131C 46 inc esi
0040131D F2:AE repne scas byte ptr es:[edi]
0040131F F7D1 not ecx
00401321 49 dec ecx
00401322 3BF1 cmp esi,ecx
00401324 ^ 72 A7 jb short ncrackme.004012CD
00401326 5F pop edi
00401327 8BC3 mov eax,ebx
00401329 5E pop esi
0040132A 5B pop ebx
0040132B 83C4 30 add esp,30
0040132E C3 retn

后面的程序基本就在比较了,看到了0040132E的retn,知道这里就返回了,由于刚才我们看到主程序中是test eax, eax就是要eax=0的时候注册成功,我们又看到00401327 mov eax, ebx。呵呵,原来eax就是ebx啊,前面对ebx的操作只有ebx置零和ebx累加,由此我们知道必须在00401311行的地方eax=edx才能保证ebx=ebx+0,再往前看,看到这里:

004012FF 0FBE4C34 2C movsx ecx,byte ptr ss:[esp+esi+2C] ; 取填入的注册码
00401304 80C2 41 add dl,41 ; 余数加65
00401307 0FBEC2 movsx eax,dl
0040130A 2BC1 sub eax,ecx

ecx的值就是我们填入的注册码,而只要让40130A时eax=ecx,后面就能保证这位计算正确,因此在这里设断,记下每次的eax,串成字符串就是所要的注册码,至于注册码的位数,看这里

0040131D F2:AE repne scas byte ptr es:[edi]
0040131F F7D1 not ecx
00401321 49 dec ecx
00401322 3BF1 cmp esi,ecx
00401324 ^ 72 A7 jb short ncrackme.004012CD

前面是取用户名的位数,后面比较,因此可以知道注册码的位数应该和用户名的位数一样。

在40130A设断点后得到的一组eax值为:
47 55 50 51 49 44 50 51 49
分别对应ASCII码为:
G U P Q I D P Q I

注册通过,后面主要参考了riijj大侠的贴子,受他贴子启发才明白了好多地方。程序基本都看懂了,改天写个注册机出来。


分类: 逆向手札 由 ssfighter 于 2006年1月3日 发表

By wynney

****************************************************************************************

http://bbs.chinadev.net

By wynney
****************************************************************************************

大家好,我是wynney,回想了一下我前面做的十五课,里面只是提了一下,没有真正介绍“最后一次异常法”。今天就给大家做个动画,希望能够起到抛砖引玉的作用。先给大家介绍一点“理论知识”吧,里面有许多观点引用了二哥的说法。

****************************************************************************************

如何分辨加密壳和压缩壳,通用特点,Od载入时有入口警告或询问是压缩程序吗?普通压缩壳Od调试时候没有异常,加密壳全部有反跟踪代码,会有许多SEH陷阱使OD调试时产生异常。
找OEP的一般思路如下:
先看壳是加密壳还是压缩壳,压缩壳相对来说容易些,一般是没有异常。
外壳解压代码起始点如果是

pushfd
pushad

跟踪时如果有发现

popad
popfd

对应
有些壳只有

pushad

popad

相对应
附近还有

retn
jmp

等指令,发生跨断跳跃一般就到了OEP处。
当然也有其他的,如 je OEP等等,一般都是段之间的大跳转,OD的反汇编窗口里都是同一个段的内容,所以更好区别是否是段间跳转。

找Oep时注意两点。
1、单步往前走,不要回头。
2、观察。注意poshad、poshfd,popad、popfd等,和外壳代码处对应,注意地址发生大的变化。单步跟踪什 么时候F8走,F7,F4步过?

这里我说说关于F8(Step Over)和F7(Step in)的一般方法,粗跟的时候一般都是常用F8走,但是有些call是变形的Jmp,此时就需要F7代过,区别是否是变形Jmp的一个简单方法是比较 call的目标地址和当前地址,如果两者离的很近,一般就是变形Jmp了,用F7走。对于Call的距离很远,可以放心用F8步过,如果你再用F7步过,只是浪费时间而已。F8步过对压缩壳用的很多,F7步过加密壳用的很多,如果用F8一不小心就跑飞(程序运行),跟踪失败。

加密壳找Oep
对于加密壳,我的方法一般是用OD载入,钩掉所有异常(不忽略任何异常,除了忽略在KERNEL32 中的内存访问异常打勾。有时由于异常过多可以适当忽略一些异常),运行,数着用了多少次Shift+F9程序运行,显然最后一次异常后,程序会从壳跳到 OEP开始执行,这就是我们寻找OEP的一个关键,如果程序 Shift+F9后直接退出,很明显加密壳检测调试器,最简单的应付方法就是用OD插件隐藏OD。
单步异常是防止我们一步步跟踪程序,即F8, F7,F4等,Int3中断是检测调试器用的,仅在Win9x系统中有效,2000/XP就会出现断点异常,其它的异常主要是干扰调试。这一系列的异常虽然干扰我们调试,但也给我们指明了一条通路,就是Shift+F9略过所有异常,然后找到最后一处异常,再它的恢复异常处下断点,跟踪到脱壳入口点。
确定从所有Seh异常中走出来,如果前面有大量循环,逐段解压。
****************************************************************************************
大家先细细品位下上面的“理论”!如果你弄懂了,那你应该高兴下了。。

****************************************************************************************


分类: 逆向手札 由 ssfighter 于 2005年11月29日 发表

用女孩子日记本在写日记,但是日记本有密码,想看…..但是看人家日记本素不厚道的做法,但是自己很想试试破解一下这个东东,后来发现其实破解这个小东东还是挺简单的,虽然软件声称“数据双重加密, 就算数据库被解了密码也无法看出日记正文,因为正文内容也用用户的密钥加密了,而且没有密码绝对还原不出原内容.”

其实,这个程序只是在数据方面加密,但是主程序问题多多,就像那个水桶原理一样,通过主程序可以很容易破解掉密码,因此纵是数据有n层加密也没用。

从网上下载了这个小程序(http://www2.skycn.com/soft/16886.html),界面还是很pp的,先赞一个。将默认密码girl改为candyfloss03。

首先用最菜鸟最菜鸟的方法,直接搜索内存。在执行diary.exe文件后,随便输入一个密码abccba,然后确定,弹出对话框“密码错误,请重新输入”,呵呵,当然密码不对了,用WinHex打开内存,搜索abccba,抬头往上看,看到什么了….“candyfloss03”几个字母赫然映入眼帘,呵呵,这就是真正的密码啊,想不到这个程序居然把输入的密码和真正的密码在内存中存储的这么近,sigh….

然后再试试用 W32Dasm反编译diary.exe看看。先用PEid0.93查一下这个程序是否加壳了。可以看到是用UPX加的壳,用upxfix去壳,再用 PEid0.93看一下,没壳了,呵呵,用W32Dasm反编译看看。长时间的等待之后,终于反编译结束了,选择菜单上的参考-串式数据参考,找到“密码错误,请重新输入”这句话,来到程序段中。记下地址005674B8,往前翻翻,在00567482处看到了命令jne 005674B8,呵呵,这就是关键跳,打开Ultraedit,搜索75348BC3,把75改为74(就是把jne改成je),存盘退出,再运行 diary.exe试试,随便输入一个密码,呵呵,这就进去了吧。

不过我还是大菜鸟大菜鸟,还只能搞搞这样的小玩意儿,还在努力学习中,写点东西纪念一下,嘻嘻。


分类: 逆向手札 由 ssfighter 于 2005年11月27日 发表

不知道为什么自己突然会对破解技术感兴趣,虽然早已经习惯了在网上找注册机,找注册码,但是自己渴望的是做一个能够自己破解软件的人,上了Crack版,发现版面很萧条,而且大部分来版上的人都是来求注册机的,可怜现在的版主也不怎么管,看了以前的帖子,有点怀念过去的版主,虽然那时候我还茫然不知破解为何物。破解应该是一种技术,正如老版主所说的,这是一个技术性的版面,但是现在已经很难见到那种纯粹技术性的文章了,有点遗憾….

不过自己是很喜欢破解这门技术的,我喜欢一个人静静地坐在电脑前面看着汇编语言一条条的执行,盯着EAX、EBX寄存器值的变化,正如每一个破解入门的教材中所讲的那样,破解永远都是违法的,我知道,对于我来说,破解只是为了一种追求自我超越的过程,是一门手艺。

呵呵,现在水平还很烂,慢慢加油~~