夜深人静,嘿嘿
只有这个时候才有时间静下来看点东西
前几天在看
16
位汇编语言程序设计
王爽
老师写的
写得真的很好
呵呵
不是卖广告哈
资源共享嘛
遇到一个问题
通过这个问题发现能深刻理解到
jmp
指令很具内涵的一些内容
甚欢
乃著此文以记之
程序如下:
assume
cs:codesg
codesg
segment
mov ax,4c00h
int 21h
start
:
mov ax,0
s:
nop
nop
mov di,offset s
;
这里应该是计算
s
对于
segment
处的偏移量,赋值给
di
mov si,offset s2
;
计算
s2
对于
segment
处的偏移,保存到
si
mov ax , cs:[si]
;
将
s
处的
1
个字节指令内容读入
ax
(注意,基址是
cs
哦
~
不是
ds
)
mov cs:[di],ax
;
将
ax
内容写到
s
处,也就是填充上边那
2
个
nop
s0:
jmp short s
;跳到
s
,那么这个时候位于
s
处指令应该是
jmp s1
;接着执行应该是
mov ax
,
0
然后
int 21
s1:
mov ax,0
int 21h
mov ax,0
s2:
jmp short s1
nop
codesg
ends
end
start
结果发现我推测的跟执行的内容完全不一样
……
晕厥
ING~
又过了一天
一觉醒来想了想这个结果
哈哈
恍然大悟
书上的例子用的是
windows
自带的
debug
调试
……
我也只会用这个了
……
用
windbg
太麻烦
od
好像只能开
32
位的
只能截图看了
jmp
指令在被编译器编译的时候会自动计算跳转时指针与目的地址的偏移量
然后通过加减
ip
这个数值实现跳转的
也就是说
jmp s
这指令实现的是相对位移跳转,跳到哪那是编译器编译的时候就计算好的了
我们来看看
1814
:
0016
这个地方的
jmp
指令
EBF0
这个指令对应的汇编语句应该是
jmp s
S
是在
1814
:
0008
处
所以反编译的结果是
jmp 0008
没错
跟我们的语句没出入
那么我们可以看看机器码
BEF0 BE
是
jmp
指令
F0
代表相对位移
以补码形式保存
那么我们计算下发现
F0
对应的
10
进制是
-16
也就是说要往后跳
16
个字节(注意是字节,
8bit
一个字节哦
~
)
1814
:
0016
这个是我们执行到
jmp
时
cs
:
ip
的地址,也就是取指令的地址
注意这个时候我们已经取出
jmp s
这个指令了
那么
IP
指针应该
+2
指向
1814
:
0018
这个位置了
往后跳
16
个字节
1814
:
0018 – 10
(
10
进制就是
16
咯)
= 1814
:
0008
应该是跳到
0008
这个位置上了
正好就是
s
对应的位置
那么我们看看
1814
:
0020
这个指令
EBF6 F6
是跳转的相对位移
补码形式存放
换算成
10
进制就是
-10
想象下当程序执行到
s0
那个时候
s
处的那
2
个
nop
指令已经被填充成
EBF6
了
根据相对位移的计算
这个时候程序运行的步骤应该是
通过
jmp s
跳转到
1814
:
0008
地址(这个时候下一个指令对应的机器码是
BEF0
)
取出下一个指令
IP+2
这个时候
IP
指向
1814
:
000a
执行
BEF6
这个指令(向后跳
10
个字节)
也就是应该跳到
1814
:
000a
– a
(
10
的
16
进制表示)
= 1814
:
0000
也就是说这个时候
jmp
指令应该是
jmp 0000
跳到
segment
开始处而不是跳到
s1
处了
接着应该是执行
mov ax,4c00h
int 21h
实际调试的结果也是这样的
如下图示
看到没
执行
jmp 0008
也就是
jmp s
以后然后就是跳到
1814
:
0000
处
而不是跳到
s1
对应的那个偏移处
接着就是跟我们想的一样
执行了
mov ax
,
4c
00h
然后就
int 21h
了
也就是说我们
jmp
的地址记录的是相对偏移量
这个程序也说明了
jmp
的地址是在运行时计算出来的而不是编译器一开始就硬编码进去的
不过当然也有硬编码进去的跳转指令啦
~~
好像是
jmp
far
地址吧
忘了的说
呵呵
只不过小小阐明下
jmp
相对跳的执行流程和细节部分
这样也有个小小启示就是以后用相对跳的时候小心咯
貌似
shellcode
编写或者改内核代码的时候可以注意下
呵呵
跳转的相对地址最好计算出来表直接就来个偏移
一不小心机子就当掉了
哇咔咔
~