一直都是在学习理论。没有实实在在的调试一个无源码软件。看到很多朋友的分析了一个扫雷的游戏。就在网上搜索了一个GTK的扫雷的游戏...自己也分析分析一下...实际上机器里面有这个游戏。但是没有源码.....为什么要找有源码的呢....呵呵。实际上是怕自己第一次分析。如果分析不出来,呵呵...准备一下。
开始:
哦,对了我的目标是给他些个外挂....不是最近外挂比较流行嘛。跟风.......
编译了获得ELF

代码:
[jun@beijihuCom mines]$ gcc mines.c -o mines `pkg-config --cflags --libs gtk+-2.0`
呵呵,先运行起来玩一下。中文支持不是很好....算了,不管了.....继续我的工作....

首先反汇编分析一下...想到的idal但他的操作实在是不习惯。以后好好的适应一下...毕竟人家可是传说中可以买到的最好的反汇编利器....
我的原始方法...objdump
先反汇编了再说
代码:
[jun@beijihuCom mines]$ objdump -d ./mines > mines.asm
再想想,我们的程序没有任何加密,也没有壳所以我们。呵呵...对了程序里面是有字符的。再dump一下
代码:
[jun@beijihuCom mines]$ objdump -s ./mines >mines.text
现在我们有了两个文件.....一个是反汇编文件,一个是字符文件。
看下我们的字符文件。界面上有个reload。这个按钮是用来重新开始的...重新开始必定要重新布雷.一定会有相关布雷的信息.....
一起看看mines.text....
代码:
.......
Contents of section .rodata:
 804a024 03000000 01000200 00000000 cddba3a1  ................
 804a034 c4e3d5e2 c3b4c5a3 a3acd3c3 cab12025  .............. %
 804a044 336420c3 eba1a300 ccabd3d0 b2c5c1cb  3d .............
 804a054 a3a1d5e2 c3b4bfec becdcde6 cdeac1cb  ................
 804a064 a1aba1ab 00200000 2564002a 00313000  ..... ..%d.*.10.
 804a074 a3a10064 656c6574 655f6576 656e7400  ...delete_event.
 804a084 72656c6f 6164004d 696e6573 3a005469  reload.Mines:.Ti
 804a094 6d653a00 30006361 6e2d666f 63757300  me:.0.can-focus.
 804a0a4 62757474 6f6e2d70 72657373 2d657665  button-press-eve
 804a0b4 6e740063 6c69636b 656400             nt.clicked.    
.......
位于0x804a084 有reload 这个是按钮的名字
位于0x804a0b7 有clicked 是用来连接按钮的 "clicked" 信号到回调函数的字符...

再到 mines.asm..... 看看....查一下0x804a084
代码:
 
 8049aae:  89 04 24               mov    %eax,(%esp)
 8049ab1:  e8 be f0 ff ff         call   8048b74 <g_signal_connect_data@plt>
 8049ab6:  c7 04 24 84 a0 04 08   movl   $0x804a084,(%esp)
 8049abd:  e8 62 f1 ff ff         call   8048c24 <gtk_button_new_with_label@plt>
 8049ac2:  a3 d4 b2 04 08         mov    %eax,0x804b2d4
 8049ac7:  a1 d4 b2 04 08         mov    0x804b2d4,%eax
 8049acc:  c7 44 24 08 1c 00 00   movl   $0x1c,0x8(%esp)
在下面是一个gtk_button_new_with_label函数,建立一个新的按钮,
代码:
movl   $0x804a084,(%esp)
指的就是给这个函数赋值,也就是 gtk_button_new_with_label("reload");返回一个对象指针
会保存在%eax里面...指针被保存到0x804b2d4
和那个0x804a0b7  "clicked" 信号
下面我们应该找到那个会调函数了..作用应该就是重新布雷了...

代码:
 
 8049ef6:  bb 81 95 04 08         mov    $0x8049581,%ebx
------------------------------------beijihu's split-line----------------------------------------------
 8049efb:  e8 34 ec ff ff         call   8048b34 <gtk_object_get_type@plt>
 8049f00:  89 c2                  mov    %eax,%edx
 8049f02:  a1 d4 b2 04 08         mov    0x804b2d4,%eax
 8049f07:  89 54 24 04            mov    %edx,0x4(%esp)
 8049f0b:  89 04 24               mov    %eax,(%esp)
 8049f0e:  e8 71 ec ff ff         call   8048b84 <g_type_check_instance_cast@plt>
------------------------------------beijihu's split-line----------------------------------------------
 8049f13:  c7 44 24 14 00 00 00   movl   $0x0,0x14(%esp)
 8049f1a:  00 
 8049f1b:  c7 44 24 10 00 00 00   movl   $0x0,0x10(%esp)
 8049f22:  00 
 8049f23:  c7 44 24 0c 00 00 00   movl   $0x0,0xc(%esp)
 8049f2a:  00 
 8049f2b:  89 5c 24 08            mov    %ebx,0x8(%esp)
 8049f2f:  c7 44 24 04 b7 a0 04   movl   $0x804a0b7,0x4(%esp)
 8049f36:  08 
 8049f37:  89 04 24               mov    %eax,(%esp)
 8049f3a:  e8 35 ec ff ff         call   8048b74 <g_signal_connect_data@plt>
 
 还记得0x804b2d4放的什么吗? 是的,按钮的句柄。
 0x804b2d4 是 g_type_check_instance_cast的参数,检测起对象的类型的正确性,返回到对象的指针到%eax实际上和强制转换有点像。
第二个分割线下面是是信号接受的函数调用,这个和masm编译的的调用有点不大一样,但是仍然是栈在传递函数,也是"c"的调用方式从右向左...
我们看到%ebx里放着回调函数的的地址....向上找找,在第一个分割线上面我们看到了会调函数的地址。

我们来到了这个地方
代码:
 08049581 <g_reset>:
 8049581:  55                     push   %ebp
 8049582:  89 e5                  mov    %esp,%ebp
 8049584:  83 ec 08               sub    $0x8,%esp
........
仔细看看......
我们根据他调用的函数猜解一下这个函数大致的意思....
<gtk_label_get_type@plt> 标签的类型
<g_type_check_instance_cast@plt> 对象检查
<gtk_label_set_text@plt> 设置标签的显示....

下面一个 jmp 到跳转的地方一个比较一个jl 跳转,显然是一个循环。不会布雷就在这里吧...继续看..
大致的看了俄看循环体里的代码甚至没有随即数的生成,不对,不会是布雷。更像是清理战场.....
果然在下面我们看到一个函数 <put_mines> 从字面上都可以看出来是布雷了。程序员们使得自己的程序易读的同时也方便了反编译的朋友们了...
代码:
 08049056 <put_mines>:
 8049056:  55                     push   %ebp
 8049057:  89 e5                  mov    %esp,%ebp
 8049059:  83 ec 18               sub    $0x18,%esp
 804905c:  e9 09 02 00 00         jmp    804926a <put_mines+0x214>
 8049061:  a1 f0 b2 04 08         mov    0x804b2f0,%eax
 8049066:  89 44 24 04            mov    %eax,0x4(%esp)
 804906a:  c7 04 24 00 00 00 00   movl   $0x0,(%esp)
 8049071:  e8 5e fb ff ff         call   8048bd4 <g_random_int_range@plt>
 8049076:  89 45 f4               mov    %eax,0xfffffff4(%ebp)
 8049079:  8b 55 f4               mov    0xfffffff4(%ebp),%edx
 804907c:  89 d0                  mov    %edx,%eax
 804907e:  c1 e0 02               shl    $0x2,%eax
 8049081:  01 d0                  add    %edx,%eax
 8049083:  c1 e0 02               shl    $0x2,%eax
 8049086:  89 c2                  mov    %eax,%edx
 8049088:  a1 c4 b2 04 08         mov    0x804b2c4,%eax
 804908d:  8d 04 02               lea    (%edx,%eax,1),%eax
 8049090:  8b 40 04               mov    0x4(%eax),%eax
 8049093:  83 f8 01               cmp    $0x1,%eax
 8049096:  0f 84 ce 01 00 00      je     804926a <put_mines+0x214>
 804909c:  8b 55 f4               mov    0xfffffff4(%ebp),%edx
........
 8049270:  a1 b0 b2 04 08         mov    0x804b2b0,%eax
 8049275:  39 c2                  cmp    %eax,%edx
 8049277:  0f 8c e4 fd ff ff      jl     8049061 <put_mines+0xb>
 804927d:  c9                     leave  
 804927e:  c3                     ret    
过然有个随机数.....哈哈。这里就应该是布雷的循环体了....一个 jmp 直接跳到了最下面...一个大循环
我们只需要了解到他的雷布完以后会保存到哪.....
实在是麻烦分析了好一会,也没有把数据结构的地址搞出来。还是动态调试一下吧.

但是我们已经知道了是
代码:
(gdb) b put_mines
Breakpoint 1 at 0x804905c
(gdb) r
.......
[Thread debugging using libthread_db enabled]
[New Thread -1208604992 (LWP 15246)]
[Switching to Thread -1208604992 (LWP 15246)]

Breakpoint 1, 0x0804905c in put_mines ()
(gdb) stepi
0x0804926a in put_mines ()                 //单步到8048bd4
(gdb) 
......
(gdb) 
0x08048bd4 in g_random_int_range@plt ()
(gdb) next
Single stepping until exit from function g_random_int_range@plt, 
which has no line number information.
0x03b29f40 in g_random_int_range () from /lib/libglib-2.0.so.0
(gdb) next
Single stepping until exit from function g_random_int_range, 
which has no line number information.
0x08049076 in put_mines ()
(gdb) info r
eax            0x54     84                      //返回一个随机数84
ecx            0x85c57e0        140269536
edx            0x0      0
ebx            0x3c16674        63006324
esp            0xbfb41170       0xbfb41170
ebp            0xbfb41188       0xbfb41188
esi            0x8049581        134518145
edi            0x85e2d68        140389736
eip            0x8049076        0x8049076 <put_mines+32>
eflags         0x246    [ PF ZF IF ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51
(gdb) c
Continuing.
跟据我的猜想应该是 第84的是地雷,注意是第八行第五个,第一个是80。继续运行后果然是这样的一个原理。但是他会保存到哪里呢?真是的,汇编分析太烦了...
一直在猜想这一段的代码是作什么的,后来搜索了一下 804b2c4 这个数字,发现他很多时候出现都会伴随着lea (%edx,%eax,1),%eax 这样一个操作,应该存放着的是一个基地址,用来寻址操作的,只是猜想,我简单的翻译了一下这一段代码
代码:
mov    %eax,0xfffffff4(%ebp)   //%eax 保存的是随机数
mov    0xfffffff4(%ebp),%edx   
mov    %edx,%eax
shl    $0x2,%eax
add    %edx,%eax
shl    $0x2,%eax
mov    %eax,%edx                 // %edx=(%eax*4+%eax)*4 相当于 随即数乘以了一个20
mov    0x804b2c4,%eax            // 是在0x804b2c4的地址处取值。
lea    (%edx,%eax,1),%eax        // %eax=%edx+%eax*1  用取得的值加上随机数成以20后的数字
mov    0x4(%eax),%eax            //在以上面计算的结果为地址的地方后推4位取一个值
cmp    $0x1,%eax                 //用上面的值于一比较
je     804926a <put_mines+0x214> //相同就转移。
后面还有和上面一些相同的代码就不详细写了,作用是赋值的,呵呵理解了。本来就想像如果是我编程的话,关于地雷会利用了一个数据结构来布置的。看来本程序的作者也是这样做的。具体的结构我们不追究了.....
分析得出 :
作者用了一个长度为20个字节的数据机构来表示地雷的相关信息,而从第五个字节开始的四个字节数据表示的是一个地雷的有无如果为一有雷,为其他的为空。而这个数据结构的基地址就保存在0x804b2c4处。(呵呵,额外的信息,我个人感觉一个雷的数据结构会有五个数据,每个4字节。因为通篇反汇编文件都是eax,ecx在操作,也就是都是4个字节的处理。没有出现cx,ch,cl的运用....题外话。)
这样我们的反汇编分析到此就算是结束了...下面我们的工作就是利用神奇的ptrace函数来实现我们的外挂了.......
代码:
//Magic Ptrace 
#include<sys/ptrace.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
 
int main(int argc,char **argv)
{  
  int addr=0,pid,i,data[100];
  pid=atoi(argv[1]);  
  ptrace(PTRACE_ATTACH,pid); 
      addr=ptrace(PTRACE_PEEKTEXT,pid,0x804b2c4); //以此地址读数据结构的基址...
      for(i=0;i<100;i++)
         data[i]=ptrace(PTRACE_PEEKTEXT,pid,addr+i*20+4);
  ptrace(PTRACE_DETACH,pid);
  for(i=0;i<100;i++)
{
if(!(i%10))
printf("\n");
printf("%d",data[i]);
}
printf("\n");
return 0;
}
//编译方法 就是最简单的 gcc -o crack.o crack.c
//先执行mines的游戏(游戏名一定要是mines),再执行以下的命令行
//./crack.o `ps -A|grep mines|awk '{print $1}'`
上面的程序不详解...结果是用01表示的1为有雷,0为无雷..有点小问题有时候程序会读不到结果...

结论:
好多朋友的都在问不懂编程能不能做逆向,实际上不是不能,只是要想有很好的提高和进步懂得编程思想是必不可少的。
本文很多地方都是在猜想作者的编程思路才分析出来的......大家共勉....
上传的附件 mines.zip