【发布时间】:2015-12-04 14:35:56
【问题描述】:
我正在尝试学习 assembly,所以我手动将 C 和 C++ 代码转换为 assembly 代码。
环境
x86
Visual Studio
目标
手动将以下C 代码转换为assembly 代码:
static HANDLE OUTPUT_HANDLE;
BOOL __stdcall InputConsole(const VOID *lpBuffer, DWORD nNumberOfCharsToWrite, LPDWORD lpNumberOfCharsWritten)
{
WriteConsoleA(OUTPUT_HANDLE, lpBuffer, nNumberOfCharsToWrite, lpNumberOfCharsWritten, 0);
}
int main()
{
DWORD charsWritten;
OUTPUT_HANDLE = GetStdHandle(STD_OUTPUT_HANDLE);
InputConsole("Hello World!\n", 13, &charsWritten);
}
尝试
.586
.model flat, stdcall
.stack 4096
EXTRN ExitProcess@4 : PROC
EXTRN GetStdHandle@4 : PROC
EXTRN WriteConsoleA@20 : PROC
.data
STD_OUTPUT_HANDLE DD ?
WRITE_CONSOLE_STRING DB "Hello World!", 10, 0
CHARS_WRITTEN DD ?
.code
WriteConsole PROC
PUSH 0 ; lpReserved
MOV EAX, [ESP + 16] ; lpNumberOfCharsWritten
PUSH EAX
MOV EAX, [ESP + 16] ; nNumberOfCharsToWrite
PUSH EAX
MOV EAX, [ESP + 16] ; lpBuffer
PUSH EAX
PUSH STD_OUTPUT_HANDLE ; hConsoleOutput
CALL WriteConsoleA@20
ADD ESP, 12 ; Restore stack
RET
WriteConsole ENDP
main: NOP
PUSH -11
CALL GetStdHandle@4;
MOV STD_OUTPUT_HANDLE, EAX
LEA EAX, CHARS_WRITTEN
PUSH EAX
PUSH 13
LEA EAX, WRITE_CONSOLE_STRING
PUSH EAX
CALL WriteConsole
PUSH 0
CALL ExitProcess@4
END main
问题
到目前为止,它确实打印了实际行,但是Access Violation Exception 被抛出ADD ESP, 12。在使用调试器仔细检查后,似乎我应该将 16 而不是 12 添加到 ESP 以恢复堆栈。仍然在创建16 之后,它会在从procedure 返回后冻结应用程序。
问题
- 我只在堆栈中压入三个 4 字节参数,为什么我需要添加
16而不是12? - 使用
ESP + xx访问“输入参数”是否正确? - 为什么我的程序会冻结,即使我正在恢复堆栈并且控制台上实际写入了一些内容?
- 是否有另一种方法可以在
assembly中“通过引用传递参数”而不是使用全局.data地址?
旁注
我知道您可以在 Visual Studio(这是我正在使用的 IDE)中为过程设置参数,但是我应该可以这样做,对吧?
【问题讨论】:
-
WriteConsoleA将自行从堆栈中删除参数,因此请尝试删除ADD ESP, 12。 -
@MikeCAT 我知道这一点,但我也需要从堆栈中删除为我的自定义过程提供的参数。因为我正在关注 __stdcall
-
add esp不能这样做,ret 12不能这样做。 -
InputConsole接受 3 个参数,因此数量为 4 * 3 = 12。RET imm16指令对于“返回并将imm16添加到ESP”很有用。 -
一共是16个,是的,返回地址4个字节,参数12个。但顺序很重要。
标签: c++ c visual-studio assembly masm32