这几天在用汇编二次开发notepad,遇到一个直接寻址方式的疑惑,百思不得其解,如果哪位了解,请告之一下怎么回事,万分谢谢!
问题是这样的:
从教科书中可以得知:mov eax, [10000000h]
如果不考虑段,简单理解就是把地址10000000h处4个字节的内容赋值给eax
接下来我在ollydbg中直接汇编“mov eax, [10000000h]”,显示汇编后的代码就是“MOV EAX, DWORD PTR DS:[10000000]”,这符合我的认知;
但是我在VS中内嵌这么一句汇编码的时候,再用Ollydbg打开查看,发现编译后的代码却是“mov eax, 10000000h”,很古怪,接下来直接写win32汇编测试用例,并用ML.exe直接编译,不过最终效果也是“mov eax, 10000000h”,这简直有点颠覆我的世界观呀;
同时“push [10000000h]”会被ML.exe直接编译成“push 10000000h”
而"mov [10000000h], eax"或"mov DWORD PTR[10000000h], eax"则直接编译报错
这些指令我原先的理解应该都寻址10000000h地址指向的4字节数据,并且不会报错;但是目前我看到只有Ollydbg的汇编功能跟我理解的一样,为什么其他内嵌VC或ML.exe编译后的结果都如此诡异???
-----------------------------------------------------------------------------------------------------------------------------
非常感谢“jianghe”及“天命小三”的回答,你们解开了我心中的疑惑和一个错误的认知(太可怕了),非常感谢!
在此我想把原先一直存在的错误认知的起因经过以及现在的新认知再列举总结下,各位看看是否还有错误;
错误认知最早的起源于沈美明汇编教材第三章讲述直接寻址方式时,有这么几句:
“
在汇编语言指令中,可以用符号地址代替数值地址,如:
mov ax, VALUE
此时VALUE为存放操作数单元的符号地址。如写成:
mov ax, [VALUE]
也是可以的,二者是等效的。
”
data1 dd ? ;定义一个DWORD数据
Func1 proc ;定义一个函数
label1: ;定义一个函数内标号
Func1 endp
上述表示中我认为data1,Func1,label1都是符号地址;
但是不知怎么搞的把“VALUE为存放操作数单元的符号地址”中的“存放操作数单元”在进入大脑后给自动过滤了,
导致在我脑海中一直有这么一个错误的认知,就是只要是符号地址,那么[VALUE]==VALUE;
因为平时还真没用过mov eax, [0x10000000]写法这种方式,所以这个错误认知一直被遗留下来;
直到最近二次开发Notepad的时候,发现call [0x10000000]或call NEAR DWORD DS:[0x10000000]在VC以及ML.exe中始终编译不过去;
上网google后,在百度知道中发现这么一个说法:call [0x10000000] 跟 call DWORD PTR [0x10000000]的效果不一样,具体见下面地址:
http://zhidao.baidu.com/question/439046121.html(现在我想他的回答应该是错误的)
在自己平时写win32汇编时,用的都是call Func1这种形式,这时看到百度知道的说法后当时就突然想到了[value]==value
然后测试如问题中描述的mov eax, [0x10000000]等写法形式,发现跟我认知的都出了问题;
因为编译后结果不是把0x10000000处存放的数据赋值给eax,而是直接把0x10000000赋值给eax,导致歧路越走越远,悲催!
现在经几位大哥指点,我想这个疑惑我明白了,同时下面仔做了一些测试验证;
以下是再次测试的结果,编译命令如下:
ML.EXE /c /coff /Cp /Fl /Fm /nologo /I"D:\RadASM\Masm32\Include" "twin1.asm"
原始测试代码:
mov eax, [10000000h]
push [10000000h]
mov eax, ds:[10000000h]
push ds:[10000000h]
mov ds:[10000000h], eax
label1:
call DWORD PTR ds:[10000000h]
call label1
mov eax, offset label1
mov eax, offset _WinMain
mov eax, offset hInstance
mov eax, label1
mov eax, _WinMain
mov eax, hInstance
mov eax, ds:[10000000h]
lea eax, label1
lea eax, _WinMain
lea eax, hInstance
lea eax, ds:[10000000h]
编译后在Ollydbg中反编译的代码:
0040132B B8 00000010 MOV EAX, 10000000
00401330 68 00000010 PUSH 10000000
00401335 A1 00000010 MOV EAX, DWORD PTR DS:[10000000]
0040133A FF35 00000010 PUSH DWORD PTR DS:[10000000]
00401340 A3 00000010 MOV DWORD PTR DS:[10000000], EAX
00401345 FF15 00000010 CALL NEAR DWORD PTR DS:[10000000]
0040134B E8 F5FFFFFF CALL twin1.00401345
00401350 B8 45134000 MOV EAX, twin1.00401345
00401355 B8 F6114000 MOV EAX, twin1.004011F6
0040135A B8 00304000 MOV EAX, twin1.00403000
0040135F B8 45134000 MOV EAX, twin1.00401345
00401364 B8 F6114000 MOV EAX, twin1.004011F6
00401369 A1 00304000 MOV EAX, DWORD PTR DS:[403000]
0040136E A1 00000010 MOV EAX, DWORD PTR DS:[10000000]
00401373 8D05 45134000 LEA EAX, DWORD PTR DS:[401345]
00401379 8D05 F6114000 LEA EAX, DWORD PTR DS:[4011F6]
0040137F 8D05 00304000 LEA EAX, DWORD PTR DS:[403000]
00401385 8D05 00000010 LEA EAX, DWORD PTR DS:[10000000]
从中我得出的结论是:
只有变量的符号地址才会使[value]==value这种等效发生,操作结果是符号地址所指向的数据,
其他的符号地址,在call|push|mov时,操作结果都是符号地址本身,并非其指向的数据;
但是对于lea指令而言,所有符号地址,对应的操作结果都是符号地址本身,即赋值给eax的值是符号地址;
call [0x10000000] 及 call NEAR DWORD PTR [0x10000000]效果是一致的,只不过写时需要加上段前缀;
补充:在网上搜到一篇关于“在masm中,直接寻址需要加段,否则会被当成立即数寻址”的文章,
http://jpkc.zzu.edu.cn/hbyycai/courses/slist.asp?id=385
[课程]Android-CTF解题方法汇总!