一个简单的linux crackme的逆向
前言
最不喜欢的就是写破解教程,酒后一时冲动,老夫卿发少年狂,许下将写一篇linux平台逆向的文章的诺言,作此文实非颇不得已。
在此申明:本文在技术上非常初级,并没有用到什么高深的技术,本人水平亦有限,如有差错,还请见谅!
开始之前的准备
正如C语言教程从 hello world 开始,我们也由一个 crackme 说开去。本文的例子程序你可以到这来下载:
http://www.crackmes.de/users/veneta/crackmes/linux_crackme_v2 。古人云“工欲善其事,必先利其器”,本文中所用到的工具及操作平台罗列如下:
操作平台: gentoo 2004.3 # kernel 2.6.9
逆向工具:
反汇编 -- objdump (这个工具基本上每个LINUX上都有)、lida( http://lida.sourceforge.net/ )
调试器 -- gdb
十六进制编辑器 -- hexedit
文本编辑器 -- vim
压缩工具 -- upx (http://upx.sourceforge.net)
计算器 -- gcalctool(gnome计算器)
开始逆向之旅
首先我们看看程序基本信息:
打开控制台,切换到程序所在目录。运行“ objdump -x cm2 ”,显示如下:
代码:
我们可以看到start address是0x08048080,但有一个问题是Sections下面却什么都没有。这不是一个正常的程序?
接下来,使用十六进制工具 hexedit 查看程序信息。运行命令:hexedit cm2 ,显示如下:
代码:
从上面的信息中可以看到程序被UPX压缩了,接下来请确信你系统中已有UPX,如果没有请到上面给出的程序链接中下载。
运行命令“ upx -d cm2 ”把程序解压缩,显示如下:
代码:
接下来,我们再使用命令“ objdump -x cm2 ”来查看程序信息。如下:
代码:
[ncc2008@localhost crack]$ objdump -x cm2
cm2: file format elf32-i386
cm2
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x080488b0
程序头:
PHDR off 0x00000034 vaddr 0x08048034 paddr 0x08048034 align 2**2
filesz 0x000000c0 memsz 0x000000c0 flags r-x
INTERP off 0x000000f4 vaddr 0x080480f4 paddr 0x080480f4 align 2**0
filesz 0x00000013 memsz 0x00000013 flags r--
LOAD off 0x00000000 vaddr 0x08048000 paddr 0x08048000 align 2**12
filesz 0x0000109c memsz 0x0000109c flags r-x
LOAD off 0x00002000 vaddr 0x0804a000 paddr 0x0804a000 align 2**12
filesz 0x00000398 memsz 0x00000ff0 flags rw-
DYNAMIC off 0x00002248 vaddr 0x0804a248 paddr 0x0804a248 align 2**2
filesz 0x000000e0 memsz 0x000000e0 flags rw-
NOTE off 0x00000108 vaddr 0x08048108 paddr 0x08048108 align 2**2
filesz 0x00000020 memsz 0x00000020 flags r--
动态节:
NEEDED libgtk-1.2.so.0
NEEDED libgdk-1.2.so.0
NEEDED libglib-1.2.so.0
NEEDED libc.so.6
INIT 0x8048758
FINI 0x8049074
HASH 0x8048128
STRTAB 0x804841c
SYMTAB 0x80481fc
STRSZ 0x225
SYMENT 0x10
DEBUG 0x0
PLTGOT 0x804a33c
PLTRELSZ 0x98
PLTREL 0x11
JMPREL 0x80486c0
REL 0x80486b8
RELSZ 0x8
RELENT 0x8
VERNEED 0x8048688
VERNEEDNUM 0x1
VERSYM 0x8048642
版本引用:
required from libc.so.6:
0x0d696911 0x00 03 GLIBC_2.1
0x0d696910 0x00 02 GLIBC_2.0
Sections:
Idx Name Size VMA LMA File off Algn
0 .interp 00000013 080480f4 080480f4 000000f4 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .note.ABI-tag 00000020 08048108 08048108 00000108 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .hash 000000d4 08048128 08048128 00000128 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .dynsym 00000220 080481fc 080481fc 000001fc 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .dynstr 00000225 0804841c 0804841c 0000041c 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .gnu.version 00000044 08048642 08048642 00000642 2**1
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .gnu.version_r 00000030 08048688 08048688 00000688 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
7 .rel.dyn 00000008 080486b8 080486b8 000006b8 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
8 .rel.plt 00000098 080486c0 080486c0 000006c0 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
9 .init 00000017 08048758 08048758 00000758 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
10 .plt 00000140 08048770 08048770 00000770 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
11 .text 000007c4 080488b0 080488b0 000008b0 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
12 .fini 0000001b 08049074 08049074 00001074 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .rodata 00000008 08049090 08049090 00001090 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
14 .eh_frame 00000004 08049098 08049098 00001098 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
15 .data 00000248 0804a000 0804a000 00002000 2**2
CONTENTS, ALLOC, LOAD, DATA
16 .dynamic 000000e0 0804a248 0804a248 00002248 2**2
CONTENTS, ALLOC, LOAD, DATA
17 .ctors 00000008 0804a328 0804a328 00002328 2**2
CONTENTS, ALLOC, LOAD, DATA
18 .dtors 00000008 0804a330 0804a330 00002330 2**2
CONTENTS, ALLOC, LOAD, DATA
19 .jcr 00000004 0804a338 0804a338 00002338 2**2
CONTENTS, ALLOC, LOAD, DATA
20 .got 0000005c 0804a33c 0804a33c 0000233c 2**2
CONTENTS, ALLOC, LOAD, DATA
21 .bss 00000c58 0804a398 0804a398 00002398 2**2
ALLOC
22 .comment 00000119 00000000 00000000 00002398 2**0
CONTENTS, READONLY
SYMBOL TABLE:
no symbols
从上面的信息中可以看出,程序使用gtk编写,程序入口start address=0x080488b0。
我们再进一步的查看程序信息,运行命令“objdump -T cm2 >iat.txt”,下面的信息将会保存到iat.txt文件中。
代码:
我们再一次运行命令“objdump -d cm2 >disasm.txt”,对程序进行反汇编,并把结果保存到disasm.txt中。
接下来,用VI打开disasm.txt文件。显示如下:
相对WINDOW平台的反汇编器,objdump 的反汇编结果在没有调试符号的情况下,不显示API的名字。下一步API调用手工的加上。
我们打开iat.txt文件,把disasm.txt文件中的地址全部转换成函数名,例如:
代码:
转换成
这样将好看多了。这一点你可以写个脚本自动完成,而不用手工一个一个的替换。或者你也可以使用更强大的反汇编工器lida来自动显示API调用名。为了方便初学者,下面使用LIDA来作为反汇编工具。用lida 载入程序,如下图:
好了,前期的工作都做了。下面让我们来运行一下程序,看看有没有什么值得参考的。运行后,出现一个窗口。如下:
如图,提示我们的CRACK任务之一就是NOP掉这个窗口。点击关闭按钮,出现第二个窗口,如下图
这是注册的主窗口。
对照LIDA中的反汇编代码加上GTK的一些基本知识,我们可以知道程序运行过程是这样的:main->跳出killme窗口->注册destroy事件处理函数->在关闭 killme 窗口时将引发destroy事件->运行回调处理函数0x80489bd->跳出crackme窗口。
我们CRACK的任务就是nop掉killme窗口,以及找出注册码。
注:GTK的相关文档你可以到这来获取 http://www.gtk.org/tutorial/
第一个任务:去除killme窗口。
用lida反汇编cm2,点击菜单 view -> Functions 找到main函数,点击进入,如下所示:
代码:
我们跟入Function___08048DF3函数,如下:
代码:
上面这个函数建立killme窗口。
为了去除这个killme窗口。我们可以在
代码:
上面的nop正好5个,我怀疑作者写crackme在这有一个对crackme函数的调用,生成程序后,他手工把这个代码NOP掉了,他是故意留出空间来做提示。因为在crackme 窗口函数中有完整的窗口初始化和退出处理,如下:
代码:
从上面的代码可以看到,如果先调用crackme 窗口的函数,那么 killme 窗口就不会出现了。
好的,下面我们就改08048988处的指令为 call 080489BD ,用计算器算一下:80489BD-08048988-5=00000030,得到代码为 e8 30 00 00 00 。嘿嘿!
好的,用hexedit 打开 cm2 程序。看看前面objdump -x cm2列出的基地址为0x8048000,所以对应的file offset应该是0x8048988-0x8048000=0x988,我们按 return 键,打开 go to 窗口,输入 0x988 ,定位到08048988处,改代码为e830000000。改完后,“ ./cm2 ”运行程序,OK。出现 crackme 窗口了。
第二个任务,找出正确的注册码。
为保持本文一个苗条的身材及加大本文所包含的信息量,我就不讲怎么根据按钮名找对应的点击事件处理函数了,讲一种简单省事的方法,找注册码最省事的方法当然是动态跟踪啦。搞不好还可以看到明码哟。费话不多说,运行命令: gdb cm2 。提示没有符号文件,不用管它。
下断点:
代码:br gtk_entry_get_text 注(类似于WIN平台下的 getdlgitemtexta )
再键入命令 “ run ”运行程序。出现了界面。
输入名字和注册码,我这用的是:
名字:ncc2008
注册码:78787878
按crackme按钮,中断到gdb中。
代码:Breakpoint 1, 0x40096eb0 in gtk_entry_get_text () from /usr/lib/libgtk-1.2.so.0
让我们看看是哪个CALL调用的,下命令"info frame",显示如下:
代码:
呵呵,清除全部断点,下命令“d”。在0x8048cc1下一个断点:
代码:br *0x8048cc1
,然后键“C”继续运行程序。
程序中断到我们所下的断点0x8048cc1处。让我们看看汇编代码:
下命令:x /10i $eip
显示如下:
代码:
看不懂?我们再看lida 中的 0x8048cc1处的代码。
代码:
接下来,我们就一步一步跟吧!整个过程单调无趣,不多说了。注册码必需是32位,我使用“12345678abcdef0123456789abcdef0”
跟踪其流程:
转换注册码“12345678abcdef0123456789abcdef0”为:
代码:
然后每一字节与"0xf0"相异或,生成:
代码:
我们在08048D8F下个断点,中断后可以看到正确的值,edx中的值为:
代码:
0x804a218处的值与0x804a3f0处的值相比较,相等就注册成功。结合前面注册码的变换,可以把上面的每字节与F0异或就是正确的注册码了,掏出计算器,计算如下:
代码:3474fb4551f2fd67ac4211395461a23c
注册码与名字无关,名字可随便写,在程序中输入上面的注册码,成功!
补丁和注册机代码就不发作者了。
全文完 *转载请注明来自看雪论坛@PEdiy.com