能力值:
( LV7,RANK:100 )
8 楼
MDebug 调试器脚本与命令行说明
第一版
2011.11.27 MDebug脚本语言是类似C/C++的64位高级语言,其编译与执行引擎内置于MDebug调试器。 1.基本原则
1.1 程序语句
1.2 注释
1.3 常量
1.4 表达式
1.5 系统内置变量
1.6 变量定义
1.7 赋值语句
1.8 判断
1.9 循环与跳转
1.10 命令
2.命令详解
2.1 系统命令
2.2 调试命令
2.3 断点
2.4 内存字符串/数据读取命令
2.5 用户接口
2.6 字符串操作命令
2.7 其他命令 1.基本原则
1.1 程序语句
每行只能写一个语句,多个语句必须分多行编写。除字符常量或字符串常量外,其他语句不区分大小写。
1.2 注释
与C/C++相同,即在/*与*/之间,或//之后的内容,例如:
/*
here is comment (这里是注释)
*/
EAX = EBX // 将EBX的值赋予EAX
1.3 常量
常量包括数字、字符、字符串。MDebug中的数字都表示16进制,不管前面有无0x,如果16进制数字的第一个数是A-F,则必须在最前面加上0或0x,否则会被当作变量处理。
下面是数字例子:
9
0x100
100 //100与0x100完全相同
00401000 //00401000与0x00401000完全相同
0xabcd1234
字符常量是在'与'符号之间的表示,与C语言的字符常量表示相同,例如:
'A' //等价于 0x41
'AB' //等价于 0x4142
'1234' //等价于 0x31323334
'ABCD' //等价于 0x41424344
字符串包括ansi字符串,unicode字符串,hex数据,例如:
"this is ansi string"
L"this is unicode string"
H"33C0B0312367DE" //内存中不可打印的数据可以表示成hex数据
1.4 表达式
表达式运算符号与C/C++相同,每个复杂表达式都是由单个基本元素与(),[],+,-,*,/,%,^,|,||,&,&&组合而成。
1.4.1 API名称、dll导出函数名称、MFC函数名称
如:
CreateFileA
MessageBoxA
ExitProcess
AfxMessageBox
MyFuncName
1.4.2 16进制数字或字符常量,如:
0a1b2
0012fe00
0x402000
76542040
'A'
'ABCD'
1.4.3 寄存器
eax,ebx,ecx,edx,esi,edi,esp,ebp,eip,ax,bx,cx,dx,si,di,sp,bp,al,ah,bl,bh,cl,ch,dl,dh
1.4.4 内存地址,以中括号表示,如:
[0012fe00]
[0x402000]
[76542040]
[eax]
[ebp]
[esp]
1.4.5 用户自定义变量(通过var 定义)
1.4.6 以上几种表达式形式之间的混合四则运算,可以包含括号,如:
MessageBoxA + 0x6
0012fe00+0x402000 + 'XXYY'
0x112233 * ([esp + 8] + edi)
0xabcd * (eax - ebx) + al + bl +[eax+4]
eax*4
[eax]+76542040
[eax+ebx+0x100] + [ebp+8]
[[[[ecx+4]+10]+0x210] + 0x98]
[ecx*4+edx-6*[esp+8]] + [eax+ebx-678] * (esi+edi) + 0x12345678/[ebp+0xc]
[[[[[[[[eax+ebx+0x99887766]]]]]]]] + edi
((([[([([eax])])]]))) 1.4.7 以上几种表达式形式之间的混合逻辑运行,如:
(eax > ebx) && (EIP >= modulebase && EIP < (modulebase + size))
(0x11223344 ^ ([eax+8] | [ecx])) && esi
([ebp+8] != 9 && [ebp+0xc] > 0x400)
(eip != CreateFileW) && (count < 0x1000000)
(eax << 0x18) >> 0x10 1.5 系统内置变量
1.5.1 result变量
在MDebug脚本执行中,部分脚本命令的返回值会放到result变量中,例如,执行以下的脚本命令:
findprocess "calc.exe" //寻找进程名称为 "calc.exe"的进程,并将pid保存到result
print result //打印result的值
会将calc.exe进程的pid保存到result,并输出到输出窗口中
1.5.2 寄存器
eax,ebx,ecx,edx,esi,edi,esp,ebp,eip,ax,bx,cx,dx,si,di,sp,bp,al,ah,bl,bh,cl,ch,dl,dh
1.6 变量定义
变量定义采用关键字 var ,在程序的任何位置都可以定义变量,变量名称必须是下划线(_), 字母、数字的组合,并且第一个字符不能是数字。变量定义采用
var name_of_var
或
var name_of_var = expression
例如: var a //定义一个变量a
var b = 0x200
var m = 'A' //定义一个变量m,并赋初始化值为 'A'
var str = "Hello, world !" //定义一个字符串变量str,并赋值
var MyUString = L"Hello ,unicode" //定义一个unicode字符串变量MyUString,并赋值
var tmp = H"112233449898AAAE34" //定义一个hex数据串
var s2 = str //定义一个变量s2,并将变量str的值赋予s2
var _x123 = 'ABCD' + 'a' + 9
var a2 = eax //定义一个变量a2,并将eax值赋予a2
var a3 = [eax + ecx] + 'A' + [[ecx+4]] + 0x0040 //定义变量a3
var a4 = result //定义一个变量a4,将result赋予a4
var a6 = (a+b) > (c+d+eax) //定义一个变量,其值为逻辑运算(a+b) > (c+d)的结果
var a5 = (eip == ExitProcess) //定义一个变量a5,初始化值为逻辑运算(eip == ExitProcess)结果,即当eip为ExitProcess时,a5为1,否则为0
变量名称不能为关键字或脚本命令,下面的定义不合法:
var result = 1 //错误!result是内置变量
var eax = 9 //错误!eax是寄存器
var if = "adf" //错误!if是关键字
var var //错误!var是关键字
var msg = "aa" //错误!msg是脚本命令
1.7 赋值语句
1.7.1 寄存器赋值, 语法为:
reg_name = expression
例如:
eax = eip //将EIP赋值给eax
esp = esp + 4 //将esp增加4
al = cl + dl + 'a'
cl = dl
bl = 1
EIP = EIP+1 //EIP 增加1
EIP = MessageBoxA //EIP修改为API MessageBoxA的地址
eax = (EIP >= x)
ecx = [ebp+8] ^ 9
edx = a+b+c+d //a,b,c,d是变量
eax = eax & 0xff
ecx = (ecx >> 8) & 0xffff00 1.7.2 对变量进行赋值,语法为
var_name = expression
例如:
x = L"hello"
var_x = eip
m = eax + [eax]
n = a+(b&c)+d^e+[k] + [k+[s+8]]
在对变量进行赋值后,变量属性自动变为当前的属性,例如:
var x = 1
print x
x = "hello,WORLD"
print x
x = H"AA1122445566"
print x
x = eip
print x
x = (100 > 200)
print x
输出为:
0x1
hello,WORLD
AA 11 22 44 55 66
0xD031ED
0x0
变量之间可以互相赋值与运算,例如:
var s = "abcXYZ"
var k = s
var m = k
print s
print k
print m
s = 0x100
k = s+1
m = s + k
print s
print k
print m
输出:
abcXYZ
abcXYZ
abcXYZ
0x100
0x101
0x201
1.7.3 对内存进行赋值,语法为:
[memory_addr] = expression
[memory_addr] = string_expressioin //向内存填写字符串/unicode 字符串/hex字符串
BYTE[memory_addr] = expression //写1个字节
WORD[memory_addr] = expression //写2个字节
QWORD[memory_addr] = expression //写8个字节
例如:
[eax] = 1
[ebp+8] = 'ABCD'
[00402000 + edx] = [ebp+8]
BYTE[eax] = al
BYTE[edx+ecx] = 'A'
BYTE[eip] = 0xC3
BYTE[eip+1] = 0x00
QWORD[edi] = 'ABCD1234'
[v1 + v2 + v3] = eax + [eax]
[esi] = "hello,world!"
[esi] = L"hello,world!"
[eax+ebx] = H"AABBCCDD33445566"
var m = "hello!"
var k = esi
[k] = m //由于m是字符串变量,[k] = m对k内存进行字符串填充
1.8 判断
语法为:
if expression
{
//....
}
其中, '{' 和 '}' 必须独占一行
例如:
if(eax == 1)
{
msg "yes,eax==1"
}
1.9 循环与跳转
1.9.1 goto 语句与标号,语法为:
labelxxx:
...
...
...
goto labelxxx 1.9.2 while/break/continue语句,语法为:
while expression
{
//...
}
与C/C++相同,break/continue必须位于while 语句的'{'与'}'范围内
例1:
//此脚本计算 1+2+3+4+...+0x999
var c = 0
var sum = 0
while(c <= 0x999)
{
sum = sum + c
c = c + 1
}
print sum 输出:
0x2E1385 例2:
//此脚本自动进行单步调试,直到运行到GetStartupInfoA为止, 并显示单步执行的数量
var count = 0
while(1)
{
if(eip == GetStartupInfoA)
{
break
}
count = count + 1
t //单步执行一条指令
}
print count 输出:
0x7D 例3:
//在进程打开文件 2.txt 的时候停下
openfile "c:\windows\notepad.exe"
bc *
bp CreateFileW
while(1)
{
run
readwstr [esp+4] //读取CreateFileW的第一个参数 (LPCTSTR lpFileName)
strstr result, "2.txt" //搜索字符串 "2.txt"
strlen result //计算搜索结果的长度
if(result > 0) //如果长度大于0,就退出
{
bc *
break
}
}
例4:
//打印所有被访问过的文件
openfile "c:\windows\notepad.exe"
bc *
bp CreateFileW
while(1)
{
run
readwstr [esp+4] //读取CreateFileW的第一个参数 (LPCTSTR lpFileName)
print result
}
例5:
//打开程序后单步运行,直到运行到 kernel32.dll 模块空间
openfile "c:\windows\notepad.exe"
var base
var size
GetModuleBase "kernel32.dll"
base = result
GetModuleSize "kernel32.dll"
size = result
while(1)
{
if((eip >= base) && (eip < (base + size))) //判断eip是否在 kernel32.dll 模块空间
{
break
}
t
} 上面的脚本,while部分也可以简化为:
while((eip < base) || (eip >= (base + size)))
{
t
} 1.10 命令
命令由主命令和参数组成,如果命令有多个参数,参数之间必须用逗号(,)进行隔开
例1:
//附加调试 calc.exe 进程
findprocess "calc.exe"
attach result 例2:
//读取当前eip内存,并打印到输出窗口
readdata eip, 0x10 //readdata 有2个参数
print result
输出
8B FF 55 8B EC 83 EC 38 A1 98 49 A6 00 33 C5 89 2.命令详解
2.1 系统命令
?/h/help
显示帮助
用法:
h
h command_name
ver
显示MDebug版本信息
exit
退出MDebug调试器
cls
清空输出窗口
sleep
Sleep 后继续执行, 时间是毫秒
用法:
sleep milliseconds
例:
Sleep 100 ; 休眠100毫秒 2.2 调试命令
openfile
打开PE文件进行调试,即使用户在选项中没有选择“打开PE文件后立即开始调试”,openfile指令也会立即开始调试
用法:
openfile file_path
openfile file_path, argument
例:
openfile "c:\windows\notepad.exe"
openfile "c:\windows\notepad.exe", "d:\2.txt"
stop
停止调试,相当于调试菜单的“停止”
pause
暂停,相当于调试菜单的“暂停”
restart
重新开始调试,相当于调试菜单的“重新开始”
attach
附加调试
用法:
attach processid
dettach
脱离调试器,相当于调试菜单的“脱离调试器”
run 或 g
运行/开始调试/运行到特定位置
用法:
run
run address
例:
run eip + 0x10
run GetStartupInfoA //运行到GetStartupInfoA处
t
单步进入 (trace into)
p
单步跳过 (trace over)
gret 或 gout
运行到返回地址处
2.3 断点
bc
删除断点
用法:
bc addr //删除addr处的断点
bc * //删除所有断点
bp
新增断点
用法:
bp addr
bp addr, condition_expression
例如:
bp 00402000
bp CreateFileA
bp GetMessageA, "[esp+8] == 0x110"
bph
设置硬件执行断点
用法
bph addr
bphr
设置硬件读内存断点
用法
bphr addr
bphr addr, size
bphw
设置硬件写内存断点
用法
bphw addr
bphw addr, size 2.4 内存字符串/数据读取命令
readdata
读内存,并将结果保存到result中
用法:
readdata addr,size
例:
//读取当前eip内存,并打印到输出窗口
readdata eip, 0x10 //readdata 有2个参数
print result
输出
8B FF 55 8B EC 83 EC 38 A1 98 49 A6 00 33 C5 89
readstr
读内存中的字符串,并将结果保存到result中
用法:
readstr addr
例:
//读 esi 指向的字符串,并打印
readstr esi
print result
readwstr
读内存中的unicode字符串,并将结果保存到result中
用法:
readwstr addr
2.5 用户接口
print 或 log
向输出窗口打印
用法:
print expression
例如:
print eax
print "abcdefg"
print result
print 'XXYY'
print [edi]
print ExitProcess
print L"abcde"
print H"AABB334455"
print a+b+c+d
print a^b
print eax+ebx > ecx
msg
显示一个消息框,用法与print或log完全相同
YESNO
显示一个消息框,要求用户选择是或否,如果选择是,result为1,否则result为0
用法:
YESNO expression
例:
YESNO "Exit now?"
if(result)
{
exit
}
getstr
显示一个对话框,要求用户输入字符串,并将结果保存到result
例如:
getstr
print result
getint
显示一个对话框,要求用户输入一个16进制整数,并将结果保存到result
例如:
getint
print result
2.6 字符串操作命令
strlen
计算字符串长度,并将结果保存到result
用法:
strlen expression
例1:
strlen "abcde"
print result
输出:
0x5
例2:
readstr [esp+4]
strlen result
print result
输出:
0x28
strcat
字符串连接,strcat 的第一个参数必须是变量
用法:
strcat dst, src
例:
var s = "abc"
strcat s, "123"
print s
输出:
abc123
strcmp/strcmpi
字符串比较,并将比较结果保存到result中。如果相同,result值=0
result保存比较的结果:
result值:
< 0 str1 < str2
= 0 str1 == str2
> 0 str1 > str2
用法:
strcmp str1,str2
strcmpi str1, str2 //比较时不关心大小写
例:
strcmp "abc", "ABC"
print result
strcmpi "abc", "ABC"
print result
输出:
0x1
0x0
strstr/strstri
字符串查找,并将查找到的结果保存到result中。result保存的是匹配到的第一个字符串
用法:
strstr str1, str2
strstri str1, str2
例1:
strstr "abcdefg", "c"
print result
strstr "abcdefg", "111"
print result
输出:
cdefg 例2:
//在进程打开文件 2.txt 的时候停下
openfile "c:\windows\notepad.exe"
bc *
bp CreateFileW
while(1)
{
run
readwstr [esp+4] //读取CreateFileW的第一个参数 (LPCTSTR lpFileName)
strstr result, "2.txt" //搜索字符串 "2.txt"
strlen result //计算搜索结果的长度
if(result > 0) //如果长度大于0,就退出
{
bc *
break
}
} strupr/strlwr
将字符串转换为大写或小写, 参数必须是变量
用法:
strupr var_name
例:
var s = "abcXYZ"
var k = s
strupr s
strlwr k
print s
print k
输出:
ABCXYZ
abcxyz
2.7 其他命令
findprocess
根据进程名称找进程的pid, 结果保存到result
用法:
findprocess expression
例:
findprocess "qq.exe"
print result
loaddll
向被调试进程远程加载dll
用法:
loaddll dll_path
例:
loaddll "d:\mydll.dll"
alloc
向被调试进程分配内存,result 保存分配的内存地址
用法:
alloc size
例:
// 向别调试进程分配内存,并将字符串"ABCDEFG"写入
alloc 1000
print result //打印内存分配地址
[result] = "ABCDEFG" //写入字符串
readstr result
print result
输出:
0x210000
ABCDEFG
GetModuleBase/GetModuleSize
取被调试进程的指定的模块的基地址或名称, 并将结果保存到result
用法:
GetModuleBase modulename
GetModuleSize modulename
例1:
var base
var size
GetModuleBase "kernel32.dll"
base = result
GetModuleSize "kernel32.dll"
size = result
print base
print size
输出:
0x75BF0000
0xDB000
例2:
//打开程序后单步运行,直到运行到 kernel32.dll 模块空间
openfile "c:\windows\notepad.exe"
var base
var size
GetModuleBase "kernel32.dll"
base = result
GetModuleSize "kernel32.dll"
size = result
while(1)
{
if((eip >= base) && (eip < (base + size))) //判断eip是否在 kernel32.dll 模块空间
{
break
}
t
}