某软件水印去除的破解记录
本站内容除转载精华分类或注明zz以外,其他均为原创内容。转载请注明出处,yhustc.com版权所有
免责声明:本文所述只为研究逆向工程技术,去水印后的软件并非用于商业用途。
某软件的Demo Version,所有功能都可以用,但是制作出的图上面全有一个水印,目标就是去掉水印。P.S.这软件是行业内的专业软件,找不到正式版下载,不然可以尝试一下使用用户名密码注册破解。
第一阶段:简单的替换方法(无效)
先查壳,没有。那么去水印最简单的办法就是用winhex把这个水印字符串用00填充。搜了一把,无果。filemon监视一下所有调用的DLL,去除系统和VC RUNTIME的,其他的搜了一把,无果。
于是想到查内存,果然打开一个图像后,该水印字符串在内存中出现了。清空后来图像上的水印字符串就没有了,估计是一个解密函数把一段什么字解密成版权然后画上去的。这是一个MDI程序,发现每开一个新窗口就会多一版水印字符串。那我想就偷点懒吧,只开一个窗口,用完就关掉,再开一个窗口。这样处理一处内存就OK了。
于是开始写代码,写了一个查找进程,把那段内存中的内容取出来清空然后覆盖回去。
- HANDLE handle = OpenProcess( PROCESS_ALL_ACCESS, FALSE, programHandle );
- //这里可能会出现error 5 拒绝访问的问题
- //解决方法是给自己进程的Token授权
- if( handle == NULL )
- {
- printError( "OpenProcess" );
- return 0;
- }
- SIZE_T dwHaveRead;
- SIZE_T dwWantRead = 18;
- LPVOID lpBuf = new char[dwWantRead];
- bool ret =ReadProcessMemory(handle,
- (LPCVOID)0x0178CB20,//基址
- lpBuf,
- dwWantRead,
- &dwHaveRead);
- if(!ret)
- {
- printError( "ReadProcessMemory Error" );//出错处理;
- }
- else
- {
- //printf("Got: %s\r\n",lpBuf);
- memset(lpBuf, 0, dwHaveRead);//修改数据lpbuf
- SIZE_T dwHaveWrite;
- SIZE_T dwWantWrite= dwWantRead;
- ret = WriteProcessMemory(handle,
- (LPVOID)0x0178CB20,//基址
- lpBuf,
- dwWantWrite,
- &dwHaveWrite);
- if(!ret)
- {
- printError( "WriteProcessMemory Error" );//出错处理;
- }
- }
- delete []lpBuf;
- CloseHandle(handle);
这个基址是用winhex查看内存,水印字符串所在地址。写好跑一把,不错,水印字符串没了。
关掉程序再打开,再跑一把这段代码,
,发现水印还在。继续用winhex看,基址变了
,除非进内存进行分页查找,每次都找到有效的基址,不然这段代码根本就没用呀。
第二阶段:OllyDBG调试(接近目标)
看来简单的替换办法是行不通了,得从程序本身入手。用OllyDBG加载后,把所有GDI32里面与加载文件并画图的相关函数全下断点,其他什么Get、Set一些东西的就不用管了。立即就来到目标地点,往下走那么几行,就发现了三处相似代码。
- 004B768E |. FF15 B01F6200 call dword ptr [<&MFC80.#876>] ; MFC80.781F3AEB
- 004B7694 |. 50 push eax
- 004B7695 |. 8B45 00 mov eax, dword ptr [ebp]
- 004B7698 |. 8D4C24 48 lea ecx, dword ptr [esp+48]
- 004B769C |. FFD0 call eax
这个call继续后,eax寄存器就是水印信息了,于是重新开始到这里跟进到call里面。只有两行内容
- 781F3AEB 8B01 mov eax, dword ptr [ecx]
- 781F3AED C3 retn
看下面的内容窗口,哈哈,原来真的是我要找的东西哦
- ds:[01770D24]=0178CA90, (ASCII "***** DEMO version")
- eax=00000012
用winhex到0178CA90这里看了一下,我猜是程序把加密字付串已经解压出来了,明文存在一个变量里,这个变量的地址就在这里了。于是修改上面的WriteProcessMemory的程序,把这个基址对应的内容清空。打开这个程序后,运行一下补丁程序,本来以为已经OK了,迫不及待的打开一张图片,发现还是有水印
。用winhex看了一下内存,原来这个地址也是变的。
回头想了一下,发现我的想法是错误的,因为这个地址所指向的水印字符串其实也是新开一个MDI子窗口后实时生成的。那么他必然会是每次都变,与上面第一种尝试其实是一样的。
第三阶段:爆破
虽然上面的尝试不成功,但是已经找到加水印的位置所在,这是没有可以肯定的。看来还是要一步步的跟踪才行。在上面call给eax赋值后,这个水印字符串被压进了栈里,再往下走四行,在" call eax "后,发现栈里面的字符串没了,有蹊跷!重来一遍跟进call eax内部,哈哈,还真是有发现,这里就是写字到图片上的过程,里面有这么一行。
- 78228A11 FF15 00181D78 call dword ptr [<&USER32.DrawTextA>] ; USER32.DrawTextA
这更加肯定了我刚才的想法,这一段代码就是水印相关的。再回头看一下代码,往下走,在写好字以后出现了一个retn,说明这个添加水印文字被封装成了一个函数。那能不能直接把这个函数给nop掉?
跟踪代码来到上层调用的地方
- 00416041 . 50 push eax
- 00416042 E8 49150A00 call 004B7590
- 00416047 . 83C4 0C add esp, 0C
这个call就是刚才的添加水印的函数。上面出现了一个push,可能是要为函数传递参数。不过通过单步运行这三行,发现push与函数无关,因为call调用完后栈里没有变化。于是尝试直接把这个call通过nop填充。填充完毕跑一下,哇哈哈,搞定了。现在任意打开新窗口加载图像,都不会有水印信息了。
总结
主要是指导方法上出现了错误,导致前两种方法的尝试花费了不少时间。因为要把程序内的文字换成别的,最简单的就是直接修改EXE,或者修改内存,但是没有考虑到这个程序这种复杂的情况。就是每开一个新的MDI子窗口,水印信息实时计算一次。这样水印信息的地址一直是在变化的。
当然如果一开始就用OllyDBG调试然后找到call添加水印的函数的地方给nop掉,又有可能出现杀鸡用牛刀的情况,比如某PDF转JPG的程序,水印是裸写在EXE里的,自己修改一下可以做成任何自己的版权信息。
时间花了一些,好在最后还是成功破解了,呵呵
小果 | 2009-11-12 14:08:30
果然很牛!!如果可以,请您加我qq吧,我需要您的帮助。
qq:173643960
谢谢!!
过客 | 2009-12-06 10:16:51
要是能做个相关的视频,就更理想啊,对新手就易学习啊乌贼 | 2010-01-27 21:54:10
请加qq 411460103 我有个类似的软件需要帮忙..