【发布时间】:2022-12-11 02:55:02
【问题描述】:
我正在研究 Windows 上 x86 系列处理器架构(32 位和 64 位)的汇编程序。并不是说我是新手,但我可能不是什么都懂,至少对 MASM 汇编程序的语法,就像它看起来的那样。我使用位于属于 Visual Studio 的文件夹中的 MASM 汇编器(用于 64 位程序):
“..\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\bin\Hostx64\x64\ml64.exe”
安装了 Visual Studio 2019,我使用其文件夹中的 MASM 汇编器。我自己有 Windows 7
我为 32 位系统制作了我的程序,它通常由 MASM 为 32 位程序组装并工作。然后我将其代码翻译为 64 位架构(那里的代码几乎不需要更改)。但是,当用 MASM 为 64 位程序组装它时,MASM 给出了错误消息,据称有一些未解析的“StartOfProgram”符号。这是控制台中的内容:
C:\Assembler>cd "C:\Assembler"
C:\Assembler>"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\bin\Hostx64\x64\ml64.exe" "C:\Assembler\Main.asm" /link /subsystem:windows /entry:StartOfProgram
Microsoft (R) Macro Assembler (x64) Version 14.29.30138.0
Copyright (C) Microsoft Corporation. All rights reserved.
Assembling: C:\Assembler\Main.asm
Microsoft (R) Incremental Linker Version 14.29.30138.0
Copyright (C) Microsoft Corporation. All rights reserved.
/OUT:Main.exe
Main.obj
/subsystem:windows
/entry:StartOfProgram
LINK : error LNK2001: unresolved external symbol StartOfProgram.
Main.exe : fatal error LNK1120: unresolved external symbols: 1
我花了大约 2 周或一个月的时间寻找此错误的解决方案,但没有找到
一般来说,他曾经给出错误消息,据称有一些未解析的符号“WinMainCRTStartup”,但最近我有点意识到他做了这样的入口点,因为我没有在控制台中明确指定入口点(通过命令“@987654326 @”,在上面的控制台中),但是有关“unresolved external symbol”的问题仍然存在,即使我在需要它的地方设置了入口点(即,在“StartOfProgram”上)
这是我的 64 位版本程序的代码,它只需要在弹出窗口中输出“Hello world”:
option casemap:none ; As far as i understand, functions from Windows API without case sensitivity not works
; **** Importing what needs ****
includelib "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\um\x64\kernel32.lib" ; Downloading main static library to use main functions of Windows API
includelib "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\um\x64\User32.lib"
extern LoadLibraryA:near ; I load from static libraries functions which used in this program
extern GetProcAddress:near
extern FreeLibrary:near
extern ExitProcess:near
; **** Declaring memory segment ****
.data
text db 'Hello world', 0 ; Text in "Text Box"'s window
header db 'Title of hello world', 0 ; Header of "Text Box"'s window
nameOfDLL db 'user32.dll', 0
nameOfProcedureOfDLL db 'MessageBoxA', 0
handlerToModule dd 0
addressOfProcedureOfDLL dq 0 ; In 64-bit operating system, addresses are 64-bit, so size of memory area that this label points to - is quad word (dq) (that is 64 bits)
.code
; **** Entry point to program ****
StartOfProgram: ; For some reason, MASM assembler recommends putting "_" sign before label of entry point to program, if it is 32-bit. Therefore, here, in 64-bit, i finally not put
push qword ptr nameOfDLL
call LoadLibraryA ; I dynamically connect DLL so that i can then take function from it
mov handlerToModule, eax
push qword ptr nameOfProcedureOfDLL
push rax ; Functions from Windows API use stdcall convention. stdcall is agreement to pass function parameters to stack backwards, so rax is last. Rax still contains Windows' DLL address (Microsoft call it "handler") (after recent call to Loadlibrary function), so it's better to use register, processor works faster with registers
call GetProcAddress
mov addressOfProcedureOfDLL, rax ; I save address of procedure that i took from GetProcAddress. In 64-bit operating system, addresses are 64-bit, so needs to transfer rax register and not eax
push 0
push qword ptr header
push qword ptr text
push 0
call qword ptr addressOfProcedureOfDLL ; It is better to immediately pass address of function through memory address label and not through register containing this address, because computer will still have to go to this address later and there is no point in wasting time reading from register of same address
push qword ptr handlerToModule
call FreeLibrary
push 0
call ExitProcess
end
这是我的这个程序的 32 位版本的代码(通常是组装和工作的):
.386 ; There indicates processor with minimal set of functions (since new Intel processors (in "x86" family of architectures) are compatible (so far) with instructions of old Intel processors of same family of architectures)
option casemap:none ; As far as i understand, functions from Windows API without case sensitivity not works
; **** Importing what needs ****
includelib "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\um\x86\kernel32.lib" ; Downloading main static library to use main functions of Windows API
;includelib "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\um\x86\User32.lib"
extern _LoadLibraryA@4:near ; I load from static libraries functions which used in this program
extern _GetProcAddress@8:near
extern _FreeLibrary@4:near
extern _ExitProcess@4:near
.model flat
; **** Declaring memory segment ****
.data
text db 'Hello world', 0 ; Text in "Text Box"'s window
header db 'Title of hello world', 0 ; Header of "Text Box"'s window
nameOfDLL db 'user32.dll', 0
nameOfProcedureOfDLL db 'MessageBoxA', 0
handlerToModule dd 0
addressOfProcedureOfDLL dd 0
.code
; **** Entry point to program ****
_StartOfProgram: ; For some reason, MASM assembler recommends putting "_" sign before label of entry point to program, if it is 32-bit
push offset nameOfDLL
call _LoadLibraryA@4 ; I dynamically connect DLL so that i can then take function from it
mov handlerToModule, eax
push offset nameOfProcedureOfDLL
push eax ; Functions from Windows API use stdcall convention. stdcall is agreement to pass function parameters to stack backwards, so eax is last. Eax still contains Windows' DLL address (Microsoft call it "handler") (after recent call to Loadlibrary function), so it's better to use register, processor works faster with registers
call _GetProcAddress@8
mov addressOfProcedureOfDLL, eax ; I save address of procedure that i took from GetProcAddress
push 0
push offset header
push offset text
push 0
call addressOfProcedureOfDLL
push handlerToModule
call _FreeLibrary@4
push 0
call _ExitProcess@4
end _StartOfProgram
这是 32 位版本程序的结果:
Result of 32-bit version of program
【问题讨论】:
-
不要只将 StartOfProgram 作为标签,而是将其声明为
StartOfProgram proc。您需要在结束前添加一个匹配的StartOfProgram endp。 -
@David Wohlferd,我只想使用我的“StartOfProgram”标签显示的入口点,因为它在 32 位 MASM 中,以及此处。至少因为我怀疑它们在某种程度上是高级的,并且作为宏,它们可以在我的程序中制作我没有输入的代码。有没有办法不用 proc 和 endp 呢?
-
由于某些原因- 可能是为了与 Windows 约定一致,即 C 名称前面加上前导下划线以获取 32 位代码中的 asm 符号名称,但不是 64 位代码中的。对于从未从 C 引用的符号名称,是的,任何一种方式都应该没问题。
-
proc/endp不应该引入额外的说明,如果你不使用任何可以实现这种情况的 MASM 东西,所以至少试一试看看 David 的建议是否有效。如果这有效但一个简单的标签不起作用,那仍然是一个有趣的问题,即为什么 MASM 在 32 位中工作时以这种方式设计。 -
虽然我不明白您对 proc/endp 的反对意见(正如 Peter 所说,他们没有添加任何说明),但如果您愿意,也可以只添加
public StartOfProgram。此外,虽然 LoadLibrary 可用于以这种方式调用函数,但还有其他选择。例如,查看您如何调用 LoadLibrary。这只是一个功能,对吧?您通过将符号声明为 extern 并在适当的库中链接来调用它。 Process Loader 负责为您加载 kernel32.dll 并查找地址。您应该能够对 MessageBoxA 执行相同的操作。
标签: windows assembly x86 x86-64 masm