刚吐血完成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
后记:似乎看起来没做多少工作,不过我觉得很累了:)也许我的精力比较差把,希望能抛砖引玉。不足之处还望大家多指点!
转载请注明出处,并保持完整性,谢谢!
- 标 题:我的diype历程之一(smartsearch保存功能之添加) (16千字)
- 作 者:菩提!
- 时 间:2002-9-25
15:51:01
- 链 接:http://bbs.pediy.com