我的diype歷程之一(smartsearch儲存功能之新增) (16千字)

看雪資料發表於2015-11-15

剛吐血完成diype那個網聰,現在又來吐血寫教程,寫完我就要去醫院輸血了,大家記得免費捐點:)
文章題目:我的diype歷程之一(smartsearch儲存功能之新增)
    作者:greenlemon(菩提!)[FCG]
破解工具:太多。。。。
破解目的:正在思考:)

diype=Dancing In Your Program Empire(最新解釋:)

首先感謝pll621大哥為我們提供了6篇高質量的diype教程,還有許多前輩們為此作的研究,都給了我動力和幫助。diype的確是一項很累人的工作,其間upfeed對我的指點功不可沒,我謹以此篇文章獻給所有幫助我的朋友們,當然也是對我勞動的總結,同時我也想以此作為給初學者的教程(但是初學者的概念很難界定,我也是初學者,很多人還稱自己是菜鳥,雖然對我來說他們已經是肉鳥:),所以如果你覺得還看不懂那麼一定是我文筆太差,而不是你水平不夠)。

言歸正傳,讓我們磨刀霍霍向網聰(希望原作者不要看到這篇文章:)

寫在前面:這裡我希望你已經具備了win32asm的基本知識,如果沒有可以檢視一下win32asm基礎教程。
          其次你應該對winapi有初步的瞭解,如果沒有至少你應該有這個winapi幫助手冊,這兩樣看雪裡都有!

網聰的這款軟體序號產生器及破解方法我以在以前的文章中說過,但是在註冊成功後,最為關鍵的儲存功能居然仍然沒有,用dede反編譯後發現那裡只有一個用來顯示什麼"付費使用者才能用此項功能"的nag而沒有其他的程式碼,說明作者沒有在這個網路下載版裡提供這項功能,這有兩種可能,1。作者根本沒在這個版裡寫程式碼,2。他寫了但是隻有付費後才會提供一個補丁修改為指向這段程式碼。(事實上在我修改程式時發現的確有一些程式碼沒有使用,可能他有寫在裡面,但是由於我已經在自己新增了,而且去尋找正確的程式碼入口也很困難,所以就沒在細究)下面我們來分析要手動新增程式碼所要做的工作:

1。回憶一下傳統的儲存功能都是怎樣的過程,首先在點選儲存後,要開啟一個對話方塊要你輸入你要儲存的檔名,這個可以用api裡getsavefilename其次在你輸入名稱後點確定,會建立一個新檔案,或者覆蓋原來的檔案,這個用createfile來實現,當然createfile只是建立檔案,你還要將資訊儲存進去,所以還要用writefile來實現,最後別忘了關閉檔案closehandle。這就是儲存檔案所要做的,當然還有記憶體的操作,這個我後面再說。

2。接下來看看我們要把什麼儲存進去,當然是我們搜尋到的電郵地址,可以通過dede很清楚地找到存放地址的控制元件是叫做stringgrid的東西,現在我們還不清楚他是怎麼儲存的,也就是說究竟以何種資料結構儲存搜尋到的結果。這是我們需要分析的。

3。我們要找到程式的空餘部分,好新增程式碼,如果你覺得不夠,還要自己加一個section,或者在原來的section上增加一些。這個工作很好做,用topo就可以:)(peter老大教的)

好我們明確了目標,就可以開始了,從最簡單的開始,3最簡單:),我們找找程式的空餘部分,我用的是4a9426開始的空間,我開始以為不夠,所以又新增了一個section,從4d4000開始。(好像差不多剛好,沒有仔細的統計,反正增加幾百位元組也無所謂,重要的是能把東西做出來:)

完成了一步很開心吧?沒有。。。太簡單了?你還要怎樣:)好進入第2,分析一下stringgrid的運作方式,開始我很迷惑,因為跟蹤後發現他存放的空間不連續,我以為那不是正確的地址,還有一塊連續的地方存放,百思不解後在upfeed的提醒下,我自己用delphi寫了一段stringgrid的程式碼,發現我的想法是對的,只不過因為這個程式用的是多執行緒,所以每個執行緒都擁有一些結果,執行緒內是連續的,執行緒之間就是不連續的分配起始地址了,看來有時候還要自己寫程式碼來分析,要不會被迷惑的,來看看它是怎麼存放的:

004A12EA  ||MOV EAX,DWORD PTR DS:[4ACC14]
004A12EF  ||PUSH EAX                        ; /Arg1 => 00BB3DB4 ASCII "sales@eyou.com"
004A12F0  ||MOV ECX,DWORD PTR DS:[4AD0C0]    ; |
004A12F6  ||XOR EDX,EDX                      ; |
004A12F8  ||MOV EAX,DWORD PTR DS:[EBX+2F8]  ; |
004A12FE  ||CALL smartsea.00468568          ; \smartsea.00468568  //這個就是設定stringgrid元素值的,跟進去看看
004A1303  ||INC DWORD PTR DS:[4AD0C0]

00468568  /$>PUSH EBP
00468569  |.>MOV EBP,ESP
0046856B  |.>PUSH ECX
0046856C  |.>PUSH EBX
0046856D  |.>PUSH ESI
0046856E  |.>PUSH EDI
0046856F  |.>MOV DWORD PTR SS:[EBP-4],ECX  //當你每次執行到這裡就會發現在[ebp+8]這個堆疊裡存放著你想要的搜尋到的郵件地址
00468572  |.>MOV ESI,EDX                      雖然它不是一個接一個的規律變化,原因剛才我講了,但是我們可以把這些指標儲存起來呀:)
00468574  |.>MOV EBX,EAX
00468576  |.>MOV EDX,DWORD PTR SS:[EBP-4]
00468579  |.>MOV EAX,EBX
0046857B  |.>CALL smartsea.004684A4
00468580  |.>MOV ECX,DWORD PTR SS:[EBP+8]
00468583  |.>MOV EDX,ESI
00468585  |.>MOV EDI,DWORD PTR DS:[EAX]
00468587  |.>CALL DWORD PTR DS:[EDI+20]
0046858A  |.>MOV CL,1
0046858C  |.>MOV EDX,ESI
0046858E  |.>MOV EAX,EBX
00468590  |.>CALL smartsea.00468440
00468595  |.>XOR ECX,ECX
00468597  |.>MOV EDX,DWORD PTR SS:[EBP-4]
0046859A  |.>MOV EAX,EBX
0046859C  |.>CALL smartsea.00468440
004685A1  |.>MOV ECX,DWORD PTR SS:[EBP-4]
004685A4  |.>MOV EDX,ESI
004685A6  |.>MOV EAX,EBX
004685A8  |.>CALL smartsea.004683F4
004685AD  |.>POP EDI
004685AE  |.>POP ESI
004685AF  |.>POP EBX
004685B0  |.>POP ECX
004685B1  |.>POP EBP
004685B2  \.>RETN 4


現在我們來做這個儲存,我原先的設想把這些指標儲存到我開闢的新的section裡,這樣不用做記憶體操作,省去一些程式碼,後來想了一想,如果以我們搜尋到1000個地址為例,因為指標是dword,也就是說需要4000位元組來存放這些指標,暈倒,所以看來不能偷懶,還是老老實實開一塊記憶體來吧。

對記憶體的操作我這樣實現,首先globalalloc一塊記憶體,然後globallock,這樣我們就可以儲存進去,最後要記得globalunlock,globalfree。(不懂就去查前面兩個手冊)那麼什麼時候申請這塊記憶體呢?我是在formcreate的時候做的,你可以在其他時候,只要保證你後面要儲存指標的時候有記憶體用。

0049B134  .>PUSH EBP                            //formcreate開始
0049B135  .>MOV EBP,ESP
0049B137  .>ADD ESP,-40
0049B13A  .>PUSH EBX
0049B13B  .>PUSH ESI
0049B13C  .>PUSH EDI
0049B13D  .>XOR ECX,ECX
0049B13F  .>MOV DWORD PTR SS:[EBP-3C],ECX
0049B142  .>MOV DWORD PTR SS:[EBP-40],ECX
0049B145  .>MOV DWORD PTR SS:[EBP-34],ECX
0049B148  .>MOV DWORD PTR SS:[EBP-38],ECX
0049B14B  .>MOV DWORD PTR SS:[EBP-30],ECX
0049B14E  .>MOV DWORD PTR SS:[EBP-28],ECX
0049B151  .>MOV DWORD PTR SS:[EBP-2C],ECX
0049B154  .>MOV DWORD PTR SS:[EBP-24],ECX
0049B157  .>MOV EBX,EAX
0049B159  .>MOV ESI,smartsea.004ACC08
0049B15E  .>XOR EAX,EAX
0049B160  .>PUSH EBP
0049B161  .>PUSH smartsea.0049B7FB
0049B166  .>PUSH DWORD PTR FS:[EAX]
0049B169  .>MOV DWORD PTR FS:[EAX],ESP
0049B16C  .>JMP smartsea.004D4000              //把mov    eax, [ebx+$0308]改成這個,跳到我們的程式碼
0049B171    >NOP
0049B172  .>XOR EDX,EDX
0049B174  .>CALL smartsea.0042D0F8

為便於理解,給出globalalloc,globallock的原型

HGLOBAL GlobalAlloc(

    UINT uFlags,    // 要申請的記憶體的標誌,也就是說這塊記憶體是什麼型別的
    DWORD dwBytes     // 要申請的記憶體大小
  );
LPVOID GlobalLock(

    HGLOBAL hMem     // 你要鎖定的那塊記憶體的指標
  );
    
004D4000  6>PUSH 0FFFFF                        //我們要這麼大的記憶體:)                     
004D4005  6>PUSH 42                            //GHND,標誌具體可以查手冊
004D4007  E>CALL <JMP.&kernel32.GlobalAlloc>    //這個函式的地址可以用WIN32ASM檢視,我這裡是4069A0
004D400C  A>MOV DWORD PTR DS:[4D4540],EAX      //把返回的地址指標儲存起來,我存到了4D4540
004D4011  5>PUSH EAX                            //我們要鎖定他
004D4012  E>CALL <JMP.&kernel32.GlobalLock>    //這個函式地址4069B8
004D4017  A>MOV DWORD PTR DS:[4D4544],EAX      //把指標儲存到4D4544
004D401C  8>MOV EAX,DWORD PTR DS:[EBX+308]      //這是原來的程式,要恢復他(這裡他沒有用到暫存器,所以我們不用儲存他們得值)
004D4022  -E>JMP smartsea.0049B172              //返回了

有了記憶體,我們就可以儲存了,這麼大一塊記憶體,我決定不僅僅儲存指標了,把那些字串全儲存起來,以後更便於寫入檔案不是嗎?

就是剛才這段:

00468568  $>PUSH EBP
00468569  .>MOV EBP,ESP
0046856B  .>PUSH ECX
0046856C  .>PUSH EBX
0046856D  .>PUSH ESI
0046856E  .>PUSH EDI
0046856F  .>JMP smartsea.004D4027        //這裡改了,跳到我們的程式
00468574  .>MOV EBX,EAX
00468576  .>MOV EDX,DWORD PTR SS:[EBP-4]
00468579  .>MOV EAX,EBX

這裡我們也用到兩個函式:一個是字串拷貝,一個是獲取字串長度,本來我想用lstrcat這個字串連線函式,但是程式裡沒有,要自己構造麻煩,所以自己來實現吧。原型如下

LPTSTR lstrcpy(

    LPTSTR lpString1,    // 目的空間指標
    LPCTSTR lpString2     // 源字串指標
  );

int lstrlen(

    LPCTSTR lpString     // 要計算長度的字串地址指標 
  );    


004D4027  PUSH EAX                      //發現他們有使用這些暫存器,而我們的操作會破壞他們的值,所以存起來先
004D4028  PUSH ECX
004D4029  PUSH EDX
004D402A  MOV EBX,DWORD PTR SS:[EBP+8]  //記得我剛才說的這裡存放著我們要的東西
004D402D  CMP EBX,0                      //這個比較是因為,你會發現第一次的值是0,可能是stringgrid的初始化,對我們沒用,而且會出錯
004D4030  JE SHORT smartsea.004D406B    //如果是0就返回去不操作
004D4032  PUSH EBX                      //源字串指標,給lstrcpy準備的
004D4033  MOV ESI,DWORD PTR DS:[4D4544]  //還記得嗎,這就是我們申請的記憶體空間指標存放處
004D4039  MOV EBX,DWORD PTR DS:[4D4548]  //這個地址用來存放一個變數,這個變數就是字串的長度,這樣,我們可以實現字串連線
004D403F  LEA EBX,DWORD PTR DS:[EBX+ESI] //上面兩個的和就指向我們要存放的目的地址的指標
004D4042  PUSH EBX                      //壓進去給lstecpy
004D4043  CALL <JMP.&kernel32.lstrcpyA>  //這個函式地址是4012F8
004D4048  PUSH DWORD PTR DS:[4D4544]    //來測一下現在我們現在的字串有多長
004D404E  CALL <JMP.&kernel32.lstrlenA>  //地址是401308
004D4053  MOV DWORD PTR DS:[4D4548],EAX  //把返回的長度值存進我們的變數空間
004D4058  MOV WORD PTR DS:[EAX+ESI],0A0D //加入回車換行,目的是在儲存的檔案裡可以一行顯示一個EMAIL
004D405E  MOV BYTE PTR DS:[EAX+ESI+2],0  //再加一個字串結束標誌NULL
004D4063  ADD EAX,2                      //由於我們加了兩個位元組,所以長度變化了
004D4066  MOV DWORD PTR DS:[4D4548],EAX  //再次儲存
004D406B  POP EDX                        //可以返回了
004D406C  POP ECX
004D406D  POP EAX
004D406E  MOV DWORD PTR SS:[EBP-4],ECX  //這個都是源程式的東西,別忘了恢復
004D4071  MOV ESI,EDX
004D4073  JMP smartsea.00468574          //回去咯

簡單的解釋一下,如果你覺得一頭霧水的話,我們的目的是儲存成這樣的形式:

green@sina.com/r/nlemon@sina.com/r/nupfeed@sina.com/r/n......
這樣我們在檔案裡才可以存成這樣:
green@sina.com
lemon@sina.com
upfeed@sina.com

再回去理解一下,不難了吧。

第二步算是搞定了,來搞最後的一步,經過了上面,發現原來也不難是吧?:)事實上這麼簡單的程式碼,我調了n次,改了n次,所以說。。。。不知道說什麼好,可能我比較菜吧:)

來看看儲存按鈕裡有什麼:

0049E688  push    $00
0049E68A  mov    cx, word ptr [$49E6A0]
0049E691  mov    dl, $02

* Possible String Reference to: '付費註冊使用者才能儲存電郵'
|
0049E693  mov    eax, $0049E6AC

|
0049E698  call    004510A4
0049E69D  ret

沒有什麼有價值的東西,全部改掉!

這是改後的:利用到messagebox原型如下

int MessageBox(

    HWND hWnd,    // 父窗體的控制程式碼
    LPCTSTR lpText,    // 要顯示的字串指標
    LPCTSTR lpCaption,    // 標題欄上字串的指標 
    UINT uType     // 訊息框的型別
  );

注意到需要父窗體控制程式碼,所以要到程式裡去找找,把斷點下在createwindowex很快就可以找到,但是我原先在這裡範了一個錯誤,我直接用的是記憶體裡的地址,忘了這對每臺機器都是變動的,現在修正了一下,我把它儲存起來了!

所以首先找到這裡:

0044A0DE  MOV EDX,EAX                              ;
0044A0E0  MOV ECX,84CA0000                        ; |
0044A0E5  MOV EAX,DWORD PTR DS:[4AAAB8]            ; |
0044A0EA  CALL smartsea.0040730C                  ; 這個進去就是creatwindowex,返回值是控制程式碼存在eax裡
0044A0EF  JMP smartsea.004D4078                    //跳到我們的儲存程式碼
0044A0F4  NOP
0044A0F5  CALL smartsea.00403B40

004D4078  MOV DWORD PTR DS:[4D4550],EAX            //存到這
004D407D  MOV DWORD PTR DS:[EBX+24],EAX            //源程式的東西恢復
004D4080  LEA EAX,DWORD PTR DS:[EBX+7C]
004D4083  JMP smartsea.0044A0F5

0049E688  call    004A9426                  //到我們做一個函式那
0049E68D  cmp    eax, +$00                //比較返回值,判斷我們是儲存還是取消
0049E690  jz      0049E69C                  //取消的話是0,就跳
0049E692  mov    eax, $004D43E9            //這裡存放著success!
0049E697  jmp    0049E6A1
0049E69C  mov    eax, $004D43E0            //這裡存著faliure!,
0049E6A1  push    $00                      //MB_OK型
0049E6A3  push    $004D4370                //這裡存著字串。。我的標誌:)
0049E6A8  push    eax                      //看看是成功還是失敗咯
0049E6A9  push    dword ptr [$4D4550]      //這個是控制程式碼,怎麼來的?當然是跟蹤來的,把斷點下在CREATEWINDOW很快就可以找到

* Reference to: user32.MessageBoxA()
|
0049E6AF  call    00401288
0049E6B4  ret

來看看我們的函式(有點長,但很簡單:)用到了這幾個函式:

BOOL GetSaveFileName(

    LPOPENFILENAME lpofn     // 指向OPENFILENAME型別的結構指標
  );

這個就是結構體,引數很多,查手冊吧
typedef struct tagOFN { // ofn 
    DWORD        lStructSize;
    HWND          hwndOwner;
    HINSTANCE    hInstance;
    LPCTSTR      lpstrFilter;
    LPTSTR        lpstrCustomFilter;
    DWORD        nMaxCustFilter;
    DWORD        nFilterIndex;
    LPTSTR        lpstrFile;
    DWORD        nMaxFile;
    LPTSTR        lpstrFileTitle;
    DWORD        nMaxFileTitle;
    LPCTSTR      lpstrInitialDir;
    LPCTSTR      lpstrTitle;
    DWORD        Flags;
    WORD          nFileOffset;
    WORD          nFileExtension;
    LPCTSTR      lpstrDefExt;
    DWORD        lCustData;
    LPOFNHOOKPROC lpfnHook;
    LPCTSTR      lpTemplateName;
} OPENFILENAME;

HANDLE CreateFile(

    LPCTSTR lpFileName,    // 檔名指標
    DWORD dwDesiredAccess,    // 授權的模式(讀,寫,還是都有)
    DWORD dwShareMode,    // 共享模式
    LPSECURITY_ATTRIBUTES lpSecurityAttributes,    // 安全屬性
    DWORD dwCreationDistribution,    // 建立模式
    DWORD dwFlagsAndAttributes,    // 檔案屬性
    HANDLE hTemplateFile     // 臨時檔案 
  );
BOOL WriteFile(

    HANDLE hFile,    // 要寫入的檔案控制程式碼,一般由CREATEFILE返回的值就是
    LPCVOID lpBuffer,    // 要寫入檔案的緩衝區指標
    DWORD nNumberOfBytesToWrite,    // 寫入多少位元組
    LPDWORD lpNumberOfBytesWritten,    // 已寫入的位元組
    LPOVERLAPPED lpOverlapped     // 重疊模式指標
  );

004A9426  push    ebp                            //儲存暫存器的值,不知道用了哪些,都存吧保險:)
004A9427  push    ebx
004A9428  push    esi
004A9429  push    edi
004A942A  mov    dword ptr [$4D4390], $0000004C  //下面是結構體的一些引數,這是整個結構體的大小,存放到從4D4390開始的一段空間裡
004A9434  push    dword ptr [$4D4550]            //這個是窗體控制程式碼,上面提到過
004A943A  pop    dword ptr [$4D4394]            //存到第二個DWORD
004A9440  push    dword ptr [$4AC4D8]            //這是模組控制程式碼,跟蹤程式開始的GETMODULEHANDLE就可以找到
004A9446  pop    dword ptr [$4D4398]            //存到第3個
004A944C  mov    dword ptr [$4D439C], $004D4350 //這個是檔案過濾的引數我們這裡是ALL FILES和TEXT FILES存在4D4350處可以用16進位制編輯器預先寫入
004A9456  mov    dword ptr [$4D43AC], $004D4420  //檔名要存放的地方
004A9460  mov    dword ptr [$4D43A8], $00000002  //預設狀態的過濾引數是TEXTFILE
004A946A  mov    dword ptr [$4D43B0], $00000104  //檔名最大長度不超過260位元組
004A9474  mov    dword ptr [$4D43C4], $00282806  //檔案的標誌,查手冊
004A947E  mov    dword ptr [$4D43C0], $004D4370  //標題欄上的字串,還是我:)
004A9488  push    $004D4390                      //結構體指標給getsavefilename

* Reference to: comdlg32.GetSaveFileNameA()
|
004A948D  call    0044C534
004A9492  cmp    eax, +$01                      //比較一下如果是取消就返回
004A9495  jnz    004A94FA                       
004A9497  push    $00                            //createfile的七個引數
004A9499  push    $20
004A949B  push    $02
004A949D  push    $00
004A949F  push    $03
004A94A1  push    $C0000000
004A94A6  push    $004D4420

* Reference to: kernel32.CreateFileA()
|
004A94AB  call    00401210
004A94B0  mov    dword ptr [$4D454C], eax        //檔案控制程式碼存起來
004A94B5  push    dword ptr [$4D4544]            //看看現在我們那個記憶體空間裡有多長的字串,好準備寫入

* Reference to: kernel32.lstrlenA()
|
004A94BB  call    00401308
004A94C0  push    $00                            //寫入檔案用的5個引數
004A94C2  push    $004D4550
004A94C7  push    eax
004A94C8  push    dword ptr [$4D4544]
004A94CE  push    dword ptr [$4D454C]

* Reference to: kernel32.WriteFile()
|
004A94D4  call    00401260
004A94D9  push    dword ptr [$4D4544]


|
004A94DF  call    004069D0我的diype歷程之一(smartsearch儲存功能之新增)
我的diype歷程之一(smartsearch儲存功能之新增)
我的diype歷程之一(smartsearch儲存功能之新增)

004A94E4  push    dword ptr [$4D4540]

* Reference to: kernel32.GlobalFree()
|
004A94EA  call    004069B0
004A94EF  push    dword ptr [$4D454C]

* Reference to: kernel32.CloseHandle()
|
004A94F5  call    00401208
004A94FA  pop    edi
004A94FB  pop    esi
004A94FC  pop    ebx
004A94FD  pop    ebp
004A94FE  ret

後記:似乎看起來沒做多少工作,不過我覺得很累了:)也許我的精力比較差把,希望能拋磚引玉。不足之處還望大家多指點!
轉載請註明出處,並保持完整性,謝謝!

相關文章