老文章档案 · 2003 年

smartsearch保存功能之添加

破解教程 · 补丁技术 · 2003-09-16 · 作者:菩提! 人气: · 出处:赢政天下整理收藏)

刚吐血完成diype那个网聪,现在又来吐血写教程,写完我就要去医院输血了,大家记得免费捐点:)

文章题目:我的diype历程之一(smartsearch保存功能之添加)

破解工具:太多。。。。

破解目的:正在思考:)

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

后记:似乎看起来没做多少工作,不过我觉得很累了:)也许我的精力比较差把,希望能抛砖引玉。不足之处还望大家多指点!

转载请注明出处,并保持完整性,谢谢! (出处:赢政天下整理收藏)

相关文章:

相关软件:

→ 特别推荐

→ 热点TOP10

我为人人《《==》》人人为我

关于我们 |

帮助(?)

| 广告服务 |

版权声明 |

商业合作 |

发展历程 |

加盟我们 |

联系我们

Copyright © 1999-2003

winzheng.com All Rights Reserved.

繁体版:由华琦数码技术提供

Powered

by:MeSky.Net

2003-2004 年,宽带刚刚普及,下载站的文章区是许多人学电脑的第一课堂。
🕰 → 🤖 从那时到现在

当年一篇教程能让一台电脑焕然一新;今天的效率工具换成了 AI。 想知道当下最可靠的 AI 模型怎么选?看 赢政指数——我们用了 28 年的选软件严谨,现在用来评测大模型。

档案性质声明:本页为赢政天下(1998-2011)老文章的历史档案复原, 内容反映当年发表时的原貌——包括当年的技术表述、软件版本与观点,部分方法在今天已不适用或不合规,仅作互联网历史记录与研究用途, 不提供任何形式的下载。原始数据经由公共网络存档(Internet Archive)复原。发现问题请联系我们
← 返回老文章档案 · 软件史档案馆 · 赢政天下站史