将字符串参数传递给PROC

时间:2019-08-03 13:41:58

标签: windows winapi x86 masm

我想调用一个函数,该函数将大写到小写转换为用户键入的字符串,并保留特殊字符。此部分有效,但仅对于前4个字符,此后的所有内容都会被截断。我相信这是因为我已将参数定义为DWORD:

我尝试使用PAGEPARABYTE。前两个不起作用,带字节的类型为missmatch。

upperToLower proc, source:dword, auxtarget:dword
  mov eax, source       ;Point to string
  mov ebx, auxtarget     ; point to destination
  L1:
  mov dl, [eax]            ; Get a character from buffer
    cmp byte ptr [eax], 0                  ; End of string? (not counters)
    je printString             ; if true, jump to printString
    cmp dl, 65                 ; 65 == 'A'
    jl notUpper                ; if less, it's not uppercase
    cmp dl, 90                 ; 90 == 'Z'
    jg notUpper                ; if greater, it's not uppercase
    xor dl, 100000b            ; XOR to change upper to lower
    mov [ebx], dl      ; add char to target
    inc eax                    ; Move counter up
    inc ebx                    ; move counter up
    jmp L1                     ; loop

    notUpper:                  ; not uppercase
    mov [ebx], dl      ; copy the letter
    inc eax            ;next letter
    inc ebx
    jmp L1

    printString:
    invoke WriteConsoleA,   consoleOutHandle, auxtarget, sizeof auxtarget, bytesWritten,    0
    ret
upperToLower endp

协议:

  upperToLower PROTO,
    source: dword,
    auxtarget: dword

调用:

    invoke upperToLower, offset buffer, offset target

buffer参数为:buffer db 128 DUP(?)

如何打印整个字符串,而不仅仅是前4个字符?

1 个答案:

答案 0 :(得分:2)

为什么只打印4个字符?您使用以下命令将字符串写入控制台:

invoke WriteConsoleA,   consoleOutHandle, auxtarget, sizeof auxtarget, bytesWritten,    0

sizeof auxtarget参数是auxtarget的大小,它是DWORD(4个字节),因此您只要求打印4个字节。您需要传递字符串的长度。您可以通过在 EAX 中获取结束地址并从中减去source指针来轻松地做到这一点。结果将是您遍历的字符串的长度。

将代码修改为:

printString:
sub eax, source
invoke WriteConsoleA,   consoleOutHandle, auxtarget, eax, bytesWritten,    0

遵循 C 调用约定的代码版本,同时使用源缓冲区和目标缓冲区,测试指针以确保它们不为NULL,并使用类似的{ {3}}如下:

upperToLower proc uses edi esi, source:dword, dest:dword 
    ; uses ESI EDI is used to tell assembler we are clobbering two of
    ; the cdecl calling convetions non-volatile registers. See:
    ; https://en.wikipedia.org/wiki/X86_calling_conventions#cdecl
    mov esi, source            ; ESI = Pointer to string
    test esi, esi              ; Is source a NULL pointer?
    jz done                    ;     If it is then we are done

    mov edi, dest              ; EDI = Pointer to string
    test edi, edi              ; Is dest a NULL pointer?
    jz done                    ;     If it is then we are done

    xor edx, edx               ; EDX = 0 = current character index into the strings

    jmp getnextchar            ; Jump into loop at point of getting next character

  charloop:
    lea ecx, [eax - 'A']       ; cl = al-'A', and we do not care about the rest
                               ;     of the register

    cmp cl, 25                 ; if(c >= 'A' && c <= 'Z') c += 0x20;
    lea ecx, [eax + 20h]       ; without affecting flags
    cmovna eax, ecx            ; take the +0x20 version if it was in the 
                               ;     uppercase range to start with
    mov [edi + edx], al        ; Update character in destination string
    inc edx                    ; Go to next character

  getnextchar:
    movzx eax, byte ptr [esi + edx]
                               ; mov al, [esi + edx] leaving high garbage in EAX is ok
                               ;     too, but this avoids a partial-register stall
                               ;     when doing the mov+sub
                               ;     in one instruction with LEA
    test eax, eax              ; Is the character NUL(0) terminator?
    jnz charloop               ;     If not go back and process character

  printString:
    ; EDI = source, EDX = length of string

    invoke WriteConsoleA, consoleOutHandle, edi, edx, bytesWritten, 0
    mov edx, sizeof buffer
  done:
    ret
upperToLower endp

采用一个参数并将源字符串更改为大写的版本可以通过以下方式完成:

upperToLower proc, source:dword
    mov edx, source            ; EDX = Pointer to string
    test edx, edx              ; Is it a NULL pointer?
    jz done                    ;     If it is then we are done

    jmp getnextchar            ; Jump into loop at point of getting next character

  charloop:
    lea ecx, [eax - 'A']       ; cl = al-'A', and we do not care about the rest
                               ;     of the register

    cmp cl, 25                 ; if(c >= 'A' && c <= 'Z') c += 0x20;
    lea ecx, [eax + 20h]       ; without affecting flags
    cmovna eax, ecx            ; take the +0x20 version if it was in the
                               ;     uppercase range to start with
    mov [edx], al              ; Update character in string
    inc edx                    ; Go to next character

  getnextchar:
    movzx eax, byte ptr [edx]  ; mov al, [edx] leaving high garbage in EAX is ok, too,
                               ;     but this avoids a partial-register stall 
                               ;     when doing the mov+sub in one instruction with LEA
    test eax, eax              ; Is the character NUL(0) terminator?
    jnz charloop               ;     If not go back and process character

  printString:
    sub edx, source            ; EDX-source=length
    invoke WriteConsoleA, consoleOutHandle, source, edx, bytesWritten, 0
  done:
    ret
upperToLower endp

观察

  • 进行字符串转换的通用upperToLower函数通常不会自行打印。通常,您会调用upperToLower仅进行转换,然后在单独的调用中将字符串输出到显示器。