首页
社区
课程
招聘
[原创]MDebug 调试器脚本与命令行说明
发表于: 2011-11-28 14:26 17027

[原创]MDebug 调试器脚本与命令行说明

2011-11-28 14:26
17027
感谢大家的支持和建议。MDebug的脚本与命令行功能已经加上,肯定有不完善的地方,希望大家多提意见和建议,有任何疑问请及时反馈。

具体帮助文件请看
http://www.mdebug.org/scripthelp.htm

请在此处下载:
http://bbs.pediy.com/showthread.php?t=142680

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (10)
雪    币: 1489
活跃值: (1013)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
2
传说中的沙发。
2011-11-28 14:58
0
雪    币: 6
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
留名,必须得
2011-11-28 15:03
0
雪    币: 1489
活跃值: (1013)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
4
希望MDebug加入类似IDA的优点为一身的调试器如:更改变量名、结构重建等。
2011-11-28 15:05
0
雪    币: 278
活跃值: (709)
能力值: ( LV15,RANK:520 )
在线值:
发帖
回帖
粉丝
5
楼主大牛非常强大,前排留名
2011-11-28 15:05
0
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
膜拜。。。。。
2011-11-28 15:29
0
雪    币: 120
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
C脚本。。。帅的无法形容。。
要是以后反汇编直接成C,,那就不用啃汇编了。
2011-11-28 15:30
0
雪    币: 1115
活跃值: (122)
能力值: ( 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
}
2011-11-28 17:25
0
雪    币: 93
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
类C脚本,很好
2011-11-30 09:40
0
雪    币: 546
活跃值: (1621)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
10
太强大了,准备入手MDebug
2011-11-30 10:24
0
雪    币: 207
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
能否兼容OD插件?如果能够支持linux就更好了
2012-12-21 10:02
0
游客
登录 | 注册 方可回帖
返回
//