RPP.EXE 是内存动态补丁制作软件。它通过脚本命令创建 win32 文件,以此文件装载程序,装载过程中等待软件解压或解除其自身保护后,然后按脚本要求修补内存中的指令,以使软件能够按我们要求运行。
如 ACDSEE3.0 是用 ASPACK 压缩软件,你 crack 时发现在内存地址 433FEA 处把 6A00 改成 EB17 就可成功,但你不可能直接修改压缩过的 ACDSEE.EXE 文件;这里就可用 RPP.EXE 按我们要求生成一 LOAD.EXE 文件,首先运行 LOAD.EXE ,它自动装载 ACDSEE.EXE, 等其自解压完成后,然后修改内存 433FEA 地址为 EB17, 这样 ACDSEE.EXE 就可按我们要求运行了,不过我不赞成用此法对付 ACDSEE.EXE ,因为 ACDSEE.EXE 运行有两种界面,其中 VIEW 界面用此法不太灵。
RPP.EXE 还可很方便对付 NAG (一些提示、警告窗口的软件),如用 NEOLITE 2.0 Y 压缩过的软件运行之前弹出来的那种窗口,当然也有专门除掉 NAG 的工具。
用法
在资源管理器中双击 rpp.exe 文件,它弹出一菜单,你选择事先编好的脚本文件,然后按 OK 。
或在 WINDOWS 下的 DOS 窗口下用命令方式“ rpp.exe <script.rpp> ”,其中 script.rpp 为脚本命令文件。
如你的输出文件己存在,它将覆盖。在命令方式下,脚本文件可是任何文件名和扩展名;但在对话窗口操作方式下,文件名必须为 *.rpp
脚 本 命 令
-------------------
';' 注释符号 , 跟着的只是些说明 , 不会执行的 , 直到下一行 . 'T=' 表示对内存的检测次数, T=1000: 意味着对内存检测 1000 次,在放弃之前,告诉你的应用程序是 不正确的版本。 注意:在脚本文件里,默认值 T=8000 。 'F=' 需要补丁的文件名 'O=' 生成的补丁文件名 , 如你没指定文件名,默认为 LOAD.EXE 'P=' 如何在内存补丁 , 格式:内存地址 / 原码 / 补码 具体看看脚本的例子 'R:' 把前面需要补的全部补好后 , 再继续 , 可以看看 azpr243.rpp 这个例子 ':' 每一行都要以冒号结尾 , 相当于回车 '$' 脚本结束标志
所有数字采用 16 进制 |
内存地址和原码都符合才会进行补丁 , 字节间请用逗号分开。脚本文件不能大于 40K, 被补的字节有限为 1f0h 字节,被处理的原文件不小于 30h 字节。
脚本例子
;script.rpp
T=1000: ; 对内存尝试 1000 次补丁 , 不行则放弃 , 如果不设默认为 8000
F=test.exe: ; 要补丁的程序
P=40101D/74,60/74,00: ; 将命令 jz xx 改为 jz next instruction
P=4024A6/46,52,45,45,20/52,21,53,43,00: ; 将字符 'FREE ' 替换成 'R!SC'
$ ; 结束
具体的脚本范例大家可参考其自带的 Scripts.zip 。
一些问题
下面是一有趣的试验,写一脚本文件,输入输出名一样,产生一执行文件,该文件将不断装载其自身
最后导致 windows 崩溃,你只好重新启动了。
'P=401000/68/B8:'
'F=fun.exe:'
'O=fun.exe:', 然后运行 fun.exe 不一会你只好重新启动微机了 :)
——————————————————————————————————
2.2 如何写Loader
作者:夜月
E-mail:luoyi.ly@yeah.net
写作日期:31th, August 2001
使用的工具
Trw2000 V1.23--Win9X Debugger
Masm32 V5.00--Loader Compiler
难易程度
Easy( ) Medium(X) Hard( ) Pro( )
----------=======Declare========----------
未经作者同意,不得修改、引用原文,一切权利保留。
本教程只供教学用,其他一切用途皆被禁止。
------------------=====Begin=====------------------
以Director 7.0为例,和大家谈谈如何写 Loader。当然,破解Director 7.0并不一定需要Loader,
它既没加壳,也没有CRC校验,完全可以用Patch的方法。但我这篇文章的目的不是教你如何破解Director,
而是教你如何写Loader。Clearly?
Ready?
Go!
Director
7.0也是一个狗保护的软件,和Authorware差不多。跟踪过程并不复杂,在此略去。
程序判断的关键点:
* Referenced by a CALL at Address:
|:00445A24
|
:0058CC24 53
push ebx
:0058CC25 32DB
xor bl, bl
:0058CC27 E835000000
call 0058CC61
:0058CC2C 83F852
cmp eax, 00000052
:0058CC2F 741D
je 0058CC4E
:0058CC31 83F856
cmp eax, 00000056
:0058CC34 7418
je 0058CC4E
:0058CC36 3D76030000
cmp eax, 00000376
:0058CC3B 7411
je 0058CC4E
:0058CC3D 3D54030000 cmp eax,
00000354
:0058CC42 740A
je 0058CC4E
:0058CC44 83F841
cmp eax, 00000041
:0058CC47 7405
je 0058CC4E
:0058CC49
83F851 cmp eax,
00000051
:0058CC4C 7502
jne 0058CC50
* Referenced by a (U)nconditional or (C)onditional
Jump at Addresses:
|:0058CC2F(C), :0058CC34(C), :0058CC3B(C), :0058CC42(C),
:0058CC47(C)
|
:0058CC4E B301
mov bl, 01
* Referenced by a (U)nconditional
or (C)onditional Jump at Address:
|:0058CC4C(C)
|
:0058CC50 E83E000000
call 0058CC93
:0058CC55 84C0
test al, al
:0058CC57 7402
je 0058CC5B
:0058CC59 B301
mov bl, 01
* Referenced by a (U)nconditional or
(C)onditional Jump at Address:
|:0058CC57(C)
|
:0058CC5B 8AC3
mov al, bl
<-----------改为 mov al,1
:0058CC5D 5B
pop ebx
:0058CC5E
C20400 ret 0004
Loader的写作思路很简单,主要是你要知道有这些个API存在。我买了一本32位汇编的书,上面
竟然说在Window98里面,不能对别的进程进行读写。幸亏有《论坛精华2》中,DDXia翻译的一篇文章上
面有介绍如何读写别的进程,不然,我到现在还被蒙在鼓里。哎!现在的书呀,真有误人子弟的嫌疑了。
写Loader要用到的几个API函数:
(1) BOOL GetOpenFileName(
LPOPENFILENAME lpofn // 指向一个OPENFILENAME的结构
);
该函数的作用是得到要被Load的程序的路径以及文件名
(2) (摘自罗云彬的Win32Asm教程)
BOOL CreateProcess(
LPCTSTR lpApplicationName, //
执行程序文件名
LPTSTR lpCommandLine, // 参数行
LPSECURITY_ATTRIBUTES lpProcessAttributes,
// 进程安全参数
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 线程安全参数
BOOL bInheritHandles,
// 继承标记
DWORD dwCreationFlags, // 创建标记
LPVOID lpEnvironment, // 环境变量
LPCTSTR lpCurrentDirectory, // 运行该子进程的初始目录
LPSTARTUPINFO lpStartupInfo,
// 创建该子进程的相关参数
LPPROCESS_INFORMATION lpProcessInformation // 创建后用于被创建子进程的信息
);
改函数的作用是创建进程,使被Load的程序运行起来
(3) BOOL ReadProcessMemory(
HANDLE hProcess,
// 被读的进程的句柄
LPCVOID lpBaseAddress, // 开始读的地址
LPVOID lpBuffer, // 读完后,数据存放的地址
DWORD
nSize, // 要读的数量,以字节为单位
LPDWORD lpNumberOfBytesRead
// 实际上读的数量,以字节为单位
);
改函数的作用是读去一片范围的内存,可以作校验用——看看运行的进程是不是我们要Load的那个。
(4) BOOL WriteProcessMemory(
HANDLE
hProcess, // 被写的进程的句柄
LPVOID lpBaseAddress, // 开始写的地址
LPVOID lpBuffer, // 要写入的数据存放地址
DWORD
nSize, // 要写的数量,以字节为单位
LPDWORD lpNumberOfBytesWritten
// 实际上写的数量,以字节为单位
);
写一个Loader要用的主要就是这4个函数。
还有一个问题:如果程序被加壳了,我们如何知道它已经在内存里面节压缩完毕?
R!sc用的是WaitForInputIdle的方法。这个方法很不错,对付大多数的壳都可以行得通。
但是,对于这个程序而言,就没有WaitForInputIdle的必要了。因为它并没有加壳。
好了,下面看看我写的这个Loader的源代码:
Loader源代码(load.exe):
---------------------------------------Cut From
Here------------------------------------------
;
;**********************************************************
; 主程序
*
;**********************************************************
.386p
.model flat,stdcall
option
casemap :none
include windows.inc
include kernel32.inc
include
user32.inc
include
comdlg32.inc
include comctl32.inc
includelib kernel32.lib
includelib user32.lib
includelib comdlg32.lib
includelib comctl32.lib
;**********************************************************
; Normal Data
*
;**********************************************************
.data
CSiR_Tag db
'PE LOADER & CRACKER-----ROBOTOW 2001',0
CSiR_Error
db 'Error!',0
CSiR_Error1 db
'Something fucked up...',0
OpenERR_txt db
'CreateProcess Error :( ',0
ReadERR_txt db
'ReadProcess Error :(',0
WriteERR_txt db
'WriteProcess Error :P',0
VersionERR_txt db
'Incorrect Version of application :(',0
CSiR_ProcessInfo
dd 4 dup(0)
CSiR_StartupInfo db
48h dup(0)
CSiR_RPBuffer db 10h
dup(0)
szFilter db 'Director.exe',0,'Director.exe',0
db
'Execute Files',0,'*.exe,;*.com',0
db 0
szTitleOpen db 'Find
Director.exe,0
szExt db '*.exe',0
;**********************************************************
;
Patch Data
*
;**********************************************************
CSiR_AppName OPENFILENAME <?>
CSiR_AppName_Buffer
db 512 dup(?)
fuck dd 58cc5bh
mysizeof dd 6h
checkbytes db 08Ah,0C3h,05Bh,0C2h,04h,00h
patch_data_1 db 0B0h,0
patch_size_1
dd 2
patch_addr_1 dd
58cc5bh
.code
Start:
invoke InitCommonControls
invoke GetModuleHandle,NULL
mov CSiR_AppName.Flags,OFN_PATHMUSTEXIST or OFN_FILEMUSTEXIST
mov CSiR_AppName.lStructSize,SIZEOF
CSiR_AppName
mov CSiR_AppName.hwndOwner,eax
mov CSiR_AppName.lpstrFilter,offset
szFilter
mov
CSiR_AppName.lpstrFile,offset CSiR_AppName_Buffer
mov CSiR_AppName.nMaxFile,512
mov CSiR_AppName.lpstrInitialDir,0
mov CSiR_AppName.lpstrTitle,offset szTitleOpen
mov CSiR_AppName.lpstrDefExt,offset
szExt
invoke GetOpenFileName,offset
CSiR_AppName
mov dword ptr [CSiR_StartupInfo],44h
invoke CreateProcessA,offset CSiR_AppName_Buffer,0,0,0,0,20h,0,0,\
offset CSiR_StartupInfo,offset CSiR_ProcessInfo
test eax,eax
jz
OpenERR
invoke ReadProcessMemory,[CSiR_ProcessInfo],[fuck],offset
CSiR_RPBuffer,\
[mysizeof],0
test eax,eax
jz ReadERR
cld
lea esi,CSiR_RPBuffer
lea
edi,checkbytes
mov ecx,6
repe cmpsb
jnz
VersionERR
Patch:
invoke
WriteProcessMemory,[CSiR_ProcessInfo],[patch_addr_1],offset patch_data_1,\
[patch_size_1],0
test eax,eax
jz WriteERR
close_this_app:
invoke CloseHandle,[CSiR_ProcessInfo]
invoke CloseHandle,[CSiR_ProcessInfo+4]
invoke ExitProcess,NULL
VersionERR:
lea eax,VersionERR_txt
jmp
abort
ReadERR:
lea eax,ReadERR_txt
jmp abort
WriteERR:
lea eax,WriteERR_txt
jmp
abort
OpenERR:
lea eax,OpenERR_txt
abort:
invoke MessageBox,0,eax,offset
CSiR_Error,0
jmp close_this_app
End Start
------------------------------------------Cut
End---------------------------------------------
------------------=====Last Words=====------------------
看过TKC教程的人都会发现,我写的这个Loader,和R!sc的相差无几。的确,R!sc的这个程序的框架
很不错,可以很方便地通过改变很小的代码而实现我们想要的不同功能。 :)
------------------=====Ending=====------------------
aNY qUESTIONS,pLEASE eMAIL tO: luoyi.ly@yeah.net
Thanks To All!Good Luck!