在论坛潜水了好长时间,终于忍不住想发表点什么了。(不然良心不安啊! )

这一篇算是偶的处女原创帖了,给大家带来的是一片关于《是男人就撑过20秒》这个游戏的分析和Patch。本人菜鸟一只,只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!

先简单说说《是男人就撑过20秒》这个游戏,就是操纵一架飞机躲避飞来的N多子弹,看你能支撑多长时间。(真的很变态 )

好,言归正传。

首先,观察游戏,发现一点,那就是每当进入游戏后鼠标就会消失。当游戏结束之后,显示你支撑的时间时,鼠标又出现了。OK,这是一个很好的线索。

用OllyDBG加载游戏,在函数ShowCursor下断。运行游戏后,游戏第一次断这里

代码:
004042F0  /$  53            push    ebx
004042F1  |.  56            push    esi
004042F2  |.  57            push    edi
004042F3  |.  55            push    ebp
004042F4  |.  83C4 D0       add     esp, -30
004042F7  |.  6A 00         push    0                                ; /Show = FALSE
004042F9  |.  E8 24050000   call    <jmp.&USER32.ShowCursor>         ;
\ShowCursor
004042FE  |.  E8 5D030000   call    00404660
00404303  |.  A1 C86D4000   mov     eax, dword ptr [406DC8]
那么,这里就应该是游戏开始,鼠标消失用的那个ShowCursor,他的参数为FALSE说明分析没错。好,继续运行,开始游戏,当游戏结束时,不出所料,游戏断下来了。

代码:
00404590  /$  53            push    ebx
00404591  |.  83C4 E4       add     esp, -1C
00404594  |.  8BD8          mov     ebx, eax
00404596  |.  6A 01         push    1                                ; /Show = TRUE
00404598  |.  E8 85020000   call    <jmp.&USER32.ShowCursor>         ; \ShowCursor
0040459D  |.  833D CC6D4000>cmp     dword ptr [406DCC], 0
跟我想的一样,参数为TRUE,鼠标显示出来,这样应该离目标不远了。接着我们看看是哪来在CALL 00404590  。

代码:
00403614  |.  85C0          test    eax, eax
00403616  |.  74 3B         je      short 00403653
00403618  |.  83F8 11       cmp     eax, 11
0040361B  |.  75 17         jnz     short 00403634
0040361D  |.  33D2          xor     edx, edx
0040361F  |.  8915 906D4000 mov     dword ptr [406D90], edx
00403625  |.  A1 DC694000   mov     eax, dword ptr [4069DC]
0040362A  |.  E8 610F0000   call    00404590                         ;  就是这里
0040362F  |.  E9 83040000   jmp     00403AB7
00403634  |>  8B15 806D4000 mov     edx, dword ptr [406D80]
0040363A  |.  0FB71455 125C>movzx   edx, word ptr [edx*2+405C12]
00403642  |.  81C2 00504000 add     edx, 00405000
00403648  |.  FF05 806D4000 inc     dword ptr [406D80]

于是,我将这个函数段跑了一遍,其中有个大循环,应该是初始化子弹的工作(由于那段代码太多,就不贴上来了 )。其实上面的代码就是游戏是否结束的关键。我想大家也都看出来了。看这代码的头两句。

00403614  |.  85C0          test    eax, eax
00403616  |.  74 3B         je      short 00403653                ;  很可疑的跳转!

这个跳转刚好跳过我们的结束代码地址00404590,所以不妨吧je改为jmp。保存后,运行游戏。发现飞机已经能够遨游在子弹群之中!!!   

好了,既然已经将关键的地方找出来了,接下来就是写一个程序Patch了。(程序很简单,也没有什么注释~~ )

代码:
      .386
      .model flat , stdcall
      option casemap : none
      
;--------------------------------------------------------------------------------

include      windows.inc
include      kernel32.inc
include      user32.inc
include      advapi32.inc
includelib    kernel32.lib
includelib    user32.lib
includelib    advapi32.lib

;--------------------------------------------------------------------------------

IDD_DLG     equ  1000
IDC_BTN     equ  1001
IDC_STC     equ  1002

;--------------------------------------------------------------------------------

      .data?
      
hInstance    dd  ?
hToken      dd  ?
luid      LUID  <>
TokenPri    TOKEN_PRIVILEGES  <>

;--------------------------------------------------------------------------------

      .data
      
szPriText    db  "提权失败",0
szFWText    db  "请先运行游戏程序",0
dwAddress    dd  00403616h
bPatch      db  0ebh
bRestore    db  74h
szWindowName    db  "",0
szStart      db  "Start Cheat",0
szStop      db  "Stop Cheat",0
bCheat      db  0  
szDebug      db  "SeSecurityPrivilege",0
;--------------------------------------------------------------------------------


      .code

_WriteProc    proc  hwndDlg,lpBuffer,dwSize
  
      LOCAL  pid,hHandle,tghwnd
      
      invoke  FindWindow,NULL,addr szWindowName
      mov  tghwnd,eax
      .if  eax == NULL
        invoke  MessageBox,hwndDlg,addr szFWText,NULL,MB_OK
        mov  eax,FALSE
        ret
      .endif
      
      invoke  GetWindowThreadProcessId,tghwnd,addr pid
      invoke  OpenProcess,PROCESS_ALL_ACCESS,FALSE,pid
      mov  hHandle,eax
      
      invoke  WriteProcessMemory,hHandle,dwAddress,lpBuffer,dwSize,NULL
      
      ret

_WriteProc endp

_Privilege    proc  hwndDlg

      
      invoke  GetCurrentProcess
      invoke  OpenProcessToken,eax,TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY ,addr hToken
      invoke  LookupPrivilegeValue,NULL,addr szDebug,addr TokenPri.Privileges.Luid
      
      mov  TokenPri.PrivilegeCount,1
      mov  TokenPri.Privileges.Attributes,SE_PRIVILEGE_ENABLED
      
      invoke  AdjustTokenPrivileges,hToken,FALSE,addr TokenPri,sizeof TokenPri,NULL,NULL
      
      .if  eax == FALSE
        invoke  MessageBox,hwndDlg,addr szPriText,NULL,MB_OK
      .endif
      
      ret

_Privilege endp

      
_ProcDlg    proc  uses ebx edi esi hwndDlg,uMsg,wParam,lParam
      
      mov  eax,uMsg
      .if  eax == WM_CLOSE
        invoke  EndDialog,hwndDlg,NULL
      .elseif  eax == WM_INITDIALOG
        invoke  _Privilege,hwndDlg    
      .elseif eax == WM_COMMAND
        mov  eax,wParam
        .if  ax == IDC_BTN
          .if  bCheat == 0
            invoke  _WriteProc,hwndDlg,addr bPatch,1
            .if  eax == TRUE
              invoke SetDlgItemText,hwndDlg,IDC_BTN,addr szStop
              mov  bCheat,1
            .endif
          .else
            invoke  _WriteProc,hwndDlg,addr bRestore,1
              invoke  SetDlgItemText,hwndDlg,IDC_BTN,addr szStart
              mov  bCheat,0
          .endif
        .endif
      .else
        mov  eax,FALSE
        ret
      .endif
      mov  eax,TRUE
      ret
      
_ProcDlg    endp


start:

      invoke  GetModuleHandle,NULL
      mov  hInstance,eax
      invoke  DialogBoxParam,hInstance,IDD_DLG,NULL,addr _ProcDlg,NULL
      invoke  ExitProcess,NULL
      
      end  start
这样,我们就随随便便撑过20秒了。o(∩_∩)o...哈哈!

程序的源码和游戏我都放在附件里了,有兴趣的朋友可以玩玩~~~
上传的附件 程序+游戏.rar