通过中间函数调用 WriteConsole 无法正常工作

Posted

技术标签:

【中文标题】通过中间函数调用 WriteConsole 无法正常工作【英文标题】:Calling WriteConsole through an intermediate function does not work properly 【发布时间】:2015-12-04 14:35:56 【问题描述】:

我正在尝试学习 assembly,所以我手动CC++ 代码转换为 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个。但顺序很重要。 【参考方案1】:

add esp 不会这样做,ret 12 会这样做。 ——小丑

【讨论】:

以上是关于通过中间函数调用 WriteConsole 无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章

C++中用writeconsole函数往控制台写字符串出现乱码,求救!

如何使用 WinApi 的 WriteConsole 打印 LOCAL 字节

WriteConsole() 奇怪的字符?

二维高斯函数(中间暗)正条纹图与环形条纹图

在JAVA,输入一个正整数,求此正整数的中间数(比如12345的中间数是3,1234则没有中间数)

4.创建中间件