下面我们写个OD脚本,实现自动修改和自动运行。
这次学习的就是编写ODbgScript脚本实现自动化调试。这是个开源插件。我们在调试器目录下OllyDBG\plugin有个ODbgScript.dll就可以调用编写的脚本自动执行。
我们从ODbgScript命令文档中可以看到它所有的命令,我们根据它的命令执行我们调试软件的步骤和过程进行封装,实现自动化执行。
大家根据下方官方提供的命令进行编写:
3. 文档----------------在这个版本中,附带了两个脚本例子(tElock098.osc 和 UPX.osc)。 这两个脚本,可以迅速找到对应壳的入口。
3.1 语言------------OllyScript脚本语言是一个种类汇编的语言。
在后面的文档中, “源操作数” 和 “目的操作数”表示以下含义: - 十六进制常数,既没有前缀也没有后缀。 (例如:是00FF, 而不是 0x00FF 和 00FFh的形式) - 变量,这个变量必须在使用前用Var进行定义 - 一个32位寄存器 (EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP, EIP)。 目前尚不支持非32位寄存器,但是您可以使用SHL/SHR 和 AND指令来获得这它们的值。 - 一个被中括号括起来的内存地址 (例如:[401000] 指向内存地址为401000里存放分数据, [ecx] 指向内存地址为寄存器ecx里存放分数据). - 一个标志位,带有感叹号前缀(!CF, !PF, !AF, !ZF, !SF, !DF, !OF) - 字符串,也可叫数据序列。其格式为: #6A0000# (数值在两个“#”号之间),两个“#”号之间必须包含至少有一个数值。 - 包含“?”通配符的字符串。比如 #6A??00# 或者 #6?0000#
33.1.1 保留变量------------------------
$RESULT-------<RESULT>保存某些函数的返回值,比如FIND函数,等等。
$VERSION--------<VERSION>保存OllyScript,的版本信息例: cmp $VERSION, "0.8" //比较是否大于 0.8版 ja version_above_08
3.1.2 指令--------------
#INC "文件名" ---------<INClude>将一个脚本文件的内容包含到另外一个脚本文件中例: #inc "anotherscript.txt"
#LOG----<LOG>开始记录运行指令指令会显示在OllyDbg的log窗口中,每条记录前都会加上“-->”的前缀例: #log
ADD 目的操作数,源操作数-------------<ADD>源操作数与目的操作数相加,并把相加的结果保存到目的操作数中。例: add x, 0F add eax, x add [401000], 5 add y, " times" // 如果在次之前y="1000" ,则在执行完此指令之后y="1000 times"
AI--<Animate Into>在OllyDbg中执行“自动步入” [Animate into]操作。例: ai
AN 地址-------<ANalyze>从指定处,对代码进行分析。例: an eip // 相当于在OllyDbg中按 Ctrl+A键
AND 目的操作数, 源操作数-------------<AND>源操作数与目的操作数进行逻辑与操作,并将结果保存到到目的操作数中。例: and x, 0F and eax, x and [401000], 5
ASK 问题------------<ASK>显示一个提示输入框,让用户输入,并将结果保存转保留变量$RESULT中(如果用户按了取消键,则$RESULT=0)。例: ask "Enter new EIP" cmp $RESULT, 0 je cancel_pressed mov eip, $RESULT
ASM 地址, 指令-----------------<ASseMble>修改指定地址的指令。并将修改后的汇编指令长度保存到保留变量$RESULT中例: asm eip, "mov eax, ecx" //将当前指令修改为 mov eax,ecx
AO--<Animate Over>在OllyDbg中执行“自动步过” [Animate over]操作。例: ao
BC 地址-------<BreakPoint Clear>清除指定地址的断点。例: bc 401000 bc x bc eip
BP addr--------<BreakPoint>在指定地址设断点例: bp 401000 bp x bp eip
BPCND 地址, 条件----------------<BreakPoint on CoNDition>在指定地址处,设置条件断点。例: bpcnd 401000, "ECX==1" //当 代码执行到401000且 ecx等于1 时,程序暂停
BPL 地址, 表达式--------------<BreakPoint of Logging>在指定地址处设置记录断点,将表达式的结果记录到记录窗口中。例: bpl 401000, "eax" // 每次执行到401000时,都将eax寄存器的结果记录
BPLCND 地址, 表达式, 条件-----------------------<BreakPoint of Logging on CoNDition>在指定地址处设置记录断点,如果条件为真时,将表达式的结果记录到记录窗口中。例: bplcnd 401000, "eax", "eax > 1" // 如果执行到401000时,满足eax>1,则将eax寄存器的结果记录
BPMC----<BreakPoint Memory Clear>清除内存断点。例: bpmc
BPHWC 地址----------<BreakPoint HardWare Clear>删除指定地址处的硬件断点。例: bphwc 401000 //清除 401000处的断点
BPHWS 地址, 模式----------------<BreakPoint HardWare Set>在指定地址,设置硬件断点。有三种模式: "r" - 读取, "w" - 写入 或者 "x" - 执行.例: bphws 401000, "x" //当执行到此地址时发生中断
BPRM 地址, 大小---------------<BreakPoint on Read Memory>在指定地址处,设置一个内存读取断点。 “大小” 是指内存中的字节大小。例: bprm 401000, FF //一个字节
BPWM 地址, 大小---------------<BreakPoint on Write Memory>在指定地址处,设置一个内存写入断点。“大小” 是指内存中的字节大小。例: bpwm 401000, FF
CMP 目的操作数, 源操作数-------------<CoMPare>比较 目的操作数与源操作数的大小,和其对应的汇编指令作用相同。例: cmp y, x cmp eip, 401000
CMT 地址, 字符串--------------<CoMmenT>在指定地址处,加入注释。例: cmt eip, "这是入口" //当前地址处 加上 “这是入口”的注释
COB---<Continue On Breakpoint>发生中断后,让脚本继续执行(移除EOB指令)例: COB
COE---<Continue On Exception>发生异常后,让脚本继续执行(移除EOE指令)例: COE
DBH---<DeBugger Hided> 隐藏调试器例: dbh
DBS---<DeBugger Show>对隐藏的调试器操作进行恢复,不再隐藏。例: dbs
DEC 变量-------<DECrement by 1>对变量进行减一操作例: dec v
DM 地址, 大小, 文件名-------------------<Dump Memory>从指定地址处开始,在内存中提取指定大小的数据,并保存到指定的文件中例: dm 401000, 1F, "c:\dump.bin"
DMA 地址, 大小, 文件名-------------------<Dump Memory Appended>从指定地址处开始,在内存中提取指定大小的数据,并保存到指定的文件中;如果指定文件已存在,则将数据追加到指定文件尾部。例: dma 401000, 1F, "c:\dump.bin"
DPE 文件名, 入口----------------<Dump Process with Entry point>提取执行模块到指定文件中。“入口”用来设定入口地址。例: dpe "c:\test.exe", eip //入口为当前地址,保存为C盘下test.exe
EOB 标签---------<Execution On Breakpoint>在下次中断发生时,跳转到指定标签处。例: eob SOME_LABEL
EOE 标签---------<Execution On Exception>在下次异常发生时,跳转到指定标签处。例: eoe SOME_LABEL
ESTI----<Exception STep Into>相当于在OllyDbg按 SHIFT-F7。例: esti
ESTO----<Exception STep cOntinue>相当于在OllyDbg按 SHIFT-F9。例: esto
EVAL----<EVALuate>计算含义变量的表达式。变量必须已经在脚本中声明。插到字符串中时,要放在用大括号{}中。结果保存在保留变量$RESULT中Sets the reserved $RESULT variable例: var x mov x, 1000 eval "x的值是 {x}" // 执行后$RESULT为 "x的值是 00001000"
EXEC/ENDE---------<EXECute/END of Execute>对当前调试进程,执行在EXEC和ENDE之间的指令。有大括号的,会被大括号中的变量的值替代。例:// 以下是做移动操作var xvar ymov x, "eax"mov y, "0DEADBEEF"execmov {x}, {y} // mov eax, 0DEADBEEF 将被执行mov ecx, {x} // mov ecx, eax 将被执行ende// 以下是调用调试程序的ExitProcess函数execpush 0call ExitProcessenderet
FIND 地址, 查找内容---------------<FIND>从指定地址开始在内存中查找指定的内容。如果查找成功,地址会保存到保留变量$RESULT中,否则$RESULT将等于 0。查找的串支持通配符“??”(见下面的例子)。
例: find eip, #6A00E8# // 查找一个Call,其的第一个参数为0 (push 0) find eip, #6A??E8# // 查找一个带参数的Call
FINDOP 地址, 查找内容-----------------<FIND OPcode>从指定地址开始查找指定一个指令,这个指令是以指定内容为开始的。 如果查找成功,地址会保存到保留变量$RESULT中,否则$RESULT将等于 0。查找的串支持通配符“??”(见下面的例子)。例: findop 401000, #61# // find next POPAD findop 401000, #6A??# // find next PUSH of something
译者注:对比一下FIND 和FINDDOP的区别:地址 数据 代码00401007 B8 3300 MOV EAX, 330040100C 33F6 XOR ESI, ESIfind 401007, #33# //$RESULT等于401008finddop 401007, #33# //$RESULT等于40100C
GN 地址-------<Get Name>获得指定地址的符号名(比如指向API函数)。符号名将保存到保留变量$RESULT中。如果符号名是一个API函数,则$RESULT_1保存链接库名(比如 kernal32)而 $RESULT_2保存符号名(比如 ExitProcess)。例: gn 401000
GPA 函数名, 动态链接库名-------------<Get Procedure Address>在指定的动态链接库中,获得指定函数的地址。如果查找成功,地址会保存到保留变量$RESULT中,否则$RESULT将等于 0。在设置API函数断点时,这个指令非常有效。例: gpa "MessageBoxA", "user32.dll" // 这条指令执行后,$RESULT等于函数MessageBoxA的地址,您可以使用"bp $RESULT"设置断点。
GO 地址-------<GO>执行到指定地址处 (相当于SoftICE中的 G 命令)例: go 401005
GMI 地址, 信息--------------<Get Module Info>获得指定地址所在模块的相关信息。“信息”可以是模块基地址[MODULEBASE], 模块大小[MODULESIZE], 代码段基地址[CODEBASE] 或者 代码段大小[CODESIZE] (如果您想在将来的版本中,获得更多的信息,请联系我)。信息会保存到保留变量$RESULT中 (如果没有找到信息,则$RESULT等于0).例: GMI eip, CODEBASE // 这条指令执行后,$RESULT等于当前所在模块的代码段基地址。
INC 变量-------<INCrement by 1>对变量进行加一操作例: inc v
JA 标签--------<Jump if Above>在cmp命令后使用. 和其对应的汇编指令作用相同.例: ja SOME_LABEL
JAE 标签---------<jump if Above or Equal>cmp. 和其对应的汇编指令作用相同.例: jae SOME_LABEL
JB 标签--------<Jump if Below>在cmp命令后使用. 和其对应的汇编指令作用相同.例: jb SOME_LABEL
JBE 标签---------<Jump if Below or Equal>在cmp命令后使用。和其对应的汇编指令作用相同.例: jbe SOME_LABEL
JE 标签--------<Jump if Equal>在cmp命令后使用. 和其对应的汇编指令作用相同.例: je SOME_LABEL
JMP 标签---------<JuMP>跳转到指定标签.例: jmp SOME_LABEL
JNE 标签---------<Jump if Not Equal>在cmp命令后使用. 和其对应的汇编指令作用相同.例: jne SOME_LABEL
LBL 地址, 字符串--------------<LaBel Insert>在指定地址处插入一个标签例: lbl eip, "NiceJump"
LOG 源操作数-------<log>将源操作数输出到OllyDbg的记录窗口[log window]中。如果源操作数 是一个字符串常量,则原样记录。如果源操作数 是一个变量或一个寄存器,则记录名称及其存放的数值例: log "Hello world" // 记录为 "Hello world" var x mov x, 10 log x // 记录为 "x = 00000010"
MOV 目的操作数, 源操作数-------------<MOVe>将源操作数移动到目的操作数中。源操作数可以是一个十六进制序列格式#某个十六进制序列#,例如:#1234#。提醒:十六进制序列的位长只能是偶数,比如2, 4, 6, 8等等。例: mov x, 0F mov y, "Hello world" mov eax, ecx mov [ecx], #00DEAD00BEEF00# mov !CF, 1 mov !DF, !PF mov [403000], "Hello world"
MSG 消息-----------<MeSsaGe>将指定消息,显示到一个对话框中。例: MSG "脚本暂停"
MSGYN message-----------<MeSsaGe Yes or No>将指定消息,显示到一个对话框中,这个对话框有“是”、“否”按钮。如果点“是”,保留变量 $RESULT 等于1,否则保留变量$RESULT等于0 。例: MSGYN "继续?"
OR 目的操作数, 源操作数-------------<OR>源操作数和目的操作数做逻辑或操作,并将结果保存到到目的操作数中。例: or x, 0F or eax, x or [401000], 5
PAUSE-----<PAUSE>暂停脚本运行。可以通过插件菜单恢复脚本运行。例: pause
REPL addr, find, repl, len--------------------------REPL 地址, 查找字符串, 替换字符串, 长度--------------------------<REPLace>在指定地址开始,在指定长度字节内,用“替换字符串”替换“查找字符串”。允许使用通配符例: repl eip, #6a00#, #6b00#, 10 repl eip, #??00#, #??01#, 10 repl 401000, #41#, #90#, 1F
RET---<RETurn>退出脚本。例: ret
RTR---<Run To Return>相当于在OllyDbg中执行 "Run to return" [Ctrl+F9]操作。例: rtr
RTU---<Run To User code>相当于在OllyDbg中执行 "Run to user code"[Alt+F9] 操作。例: rtu
RUN---<RUN>相当于在OllyDbg中按 F9。例: run
SHL 目的操作数, n-------------左移目的操作数,n比特位;并将结果保存到到目的操作数中。例: mov x, 00000010 shl x, 8 // x is now 00001000
SHR目的操作数, n-------------<SHift Right>右移目的操作数,n 比特位;并将结果保存到到目的操作数中。例: mov x, 00001000 shr x, 8 // x is now 00000010
STI---<STep Into>相当于在OllyDbg中按 F7,单步步入。例: sti
STO---<STep Over>相当于在OllyDbg中按 F8,单步步过。例: sto
SUB dest, src-------------Substracts src from dest and stores result in destExample: sub x, 0F sub eax, x sub [401000], 5
TI--<Trace Into>相当于在OllyDbg中执行 "Trace into" 操作。例: ti
TICND cond----------<Trace Into Condition>执行 "Trace into" 操作,直到条件为真时停止。例: ticnd "eip > 40100A" // 当 eip > 40100A 时停止
TO--<Trace Over>相当于在OllyDbg中执行 "Trace over" 操作。例: to
TOCND cond----------<Trace Over Condition>执行 "Trace over" 操作,直到条件为真时停止。例: tocnd "eip > 40100A" // 当 eip > 40100A 时停止
VAR---<VARiable>在脚本中,声明一个变量。必须在变量使用先声明。例: var x
XOR 目的操作数, 源操作数-------------<XOR>源操作数与目的操作数进行异或操作,并将结果保存到到目的操作数中。例: xor x, 0F xor eax, x xor [401000], 5
3.2 标签----------定义标签,要在标签名后面要加上一个冒号.例: SOME_LABEL:
3.3 注释------------您可以使用“//”在任何地方进行注释。块注释必须另外起一行并以 “/*”做为开始,以“*/”作为结束,“*/”也必须另起一行。
例:/*您的注释*/
例如我们以前篇中改加密视频机器码的实例进行编写脚本实现自动处理和修改。 首先我们在调试器内的手动操作修改固定机器码的步骤为:4.修改完成后,F9,只要程序断在出现机器码的断点上,我们就手动修改机器码以上是手动修改的过程。这样重复修改的过程,我们就可以编写脚本自动执行。BP addr--------<BreakPoint>在指定地址设断点
ESTO----<Exception STep cOntinue>相当于在OllyDbg按 SHIFT-F9。例: esto
MOV 目的操作数, 源操作数-------------<MOVe>将源操作数移动到目的操作数中。源操作数可以是一个十六进制序列格式#某个十六进制序列#,例如:#1234#。提醒:十六进制序列的位长只能是偶数,比如2, 4, 6, 8等等。
找到相关的命令后,就可以动手编写了,是不是也非常简单。命令也给了使用说明。
bp 0047A599 estojmp _loop
CheckO: mov [edx],"2b6acd9ce2-665fc74da5-0000000000"estojmp _loop
_loop:cmp eip,0047A599je CheckOESTOjmp _loop
脚本编写后,我们启动调试器,拖入EXE加密视频后,在反编写窗口中右键找到脚本插件 脚本流程就是对机器码位置下断点,一直监控当前地址和断点地址判断,然后修改EDX的值。自动执行脚本。 根据命令,我们可以单步执行命令,看看有没错的地方。有错误它会报,然后我们修改即可。 如果脚本无误,按空格键它就会自动执行脚本去修改机器码了。这样每次启动脚本自动执行就固定了机器码,是不是非常方便。
阅读原文:原文链接
该文章在 2026/3/17 18:38:15 编辑过