繁体   English   中英

如何在不需要 C 库的情况下在汇编程序中运行 Windows shell 命令?

[英]How can I run a Windows shell command in an assembly program, without needing a C library?

我正在学习汇编,但我一直在学习 Linux,自从我了解 C/C++ 以来,它与 C/C++ 非常交织。

但是,现在,我想在我的 Windows 计算机上编写一个汇编程序,该程序调用一个运行 shell 命令的函数,并且希望不包含任何库,因为我希望这个可执行文件尽可能小 (我制作的一个包含cstdlib的简单 C++ 可执行文件是 32 KB,而我几乎没有输入任何代码。)

我想基本上写这个程序,但在汇编中:

#include <cstdlib>

int main()
{
    system("echo ABCDEFG> msg.txt");
    system("type msg.txt");
    return 0;
}

到目前为止,我已经安装了 NASM 和 MinGW,并且我正在尝试使用它们 [编辑:我现在正尝试使用 MASM,与 Visual Studio 一起使用],但没有任何效果,我认为这是因为 Windows 拥有来自 Microsoft 的自己的东西显然不在 Linux 中,但我遇到的在线资源都没有成功编译我想要做的事情。 它似乎也不理解语句extern system ,所以我认为这可能是 C/C++ 库的东西(来自cstdlib )。

此外,在某些情况下,我遇到了一个错误,它说“字符常量太长”。 我假设它指的是字符串; 在实际程序中,要回显的字符串比上面示例中的要长得多。

生成的 .obj 文件显然也有问题。

因此,基本上,我所需要的只是如何将上述程序转换为 Windows 程序集,以及如何让 NASM 和 MinGW 将其转换为可执行文件而不会出现错误。

编辑:经过几天的研究,我决定我不知道自己做错了什么,也不知道什么是对的。

我现在尝试过 Visual Studio,但又一次,它不理解extern system

.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode: DWORD

.data
    Var db  'dir && pause', 0

.code
extern system

main PROC
    push    ebp
    mov     ebp, esp
    sub     esp, 32
    lea     ecx, [Var]
    call    system
    xor     eax, eax
    INVOKE  ExitProcess, 0
main ENDP
END main

正如我在评论中提到的,Windows 确实有一个系统命令。 它位于 MSVCRT.dll 中,因此您需要与 msvcrt.lib 链接以解析外部引用。

您从“第 11 行”得到的错误是由于错误地定义了外部,这很奇怪,因为您在几行之前正确地对 ExitProcess(也是外部)执行了相同的操作。

32k 听起来可能很大,但 Windows 现在可以在 4k 页面上工作,允许它设置页面保护位(只读页面、可执行页面等)。 一页用于可执行代码,一页用于数据,一页用于常量、导入、导出、调试信息、异常信息等。按照今天的标准,8 页似乎并不那么离谱。

但正如我也提到的,如果有一些令人信服的理由,可以使用一些技巧来减小可执行文件的大小。 此示例(使用nasm.exe doit.asm -o doit.exe使用 NASM 版本 2.15.05 构建)为 1,184 字节。 当您运行它时,似乎什么都没有发生(没有打开 cmd 窗口)而且速度非常快。 但是您会看到 foo.xxx 被创建并包含来自代码中嵌入的dir > foo.xxx命令的输出。

这里有很多链接器通常会为您处理的 gobbledygook。 有趣的部分从ENTRY:开始。 cmd 在代码末尾附近定义。

此代码的隐秘性与小尺寸相结合,为使用此代码进行恶作剧提供了可能性。 我会假设这不是你的意图。

; Check for NASM version at least 2.15.05
%if __?NASM_VERSION_ID?__ < 0x0020F0500
%error "Newer version of nasm required"
%endif

%define RoundTo(a, b) ((((a) + ((b) - 1)) / (b)) * (b))
%define Stringify(&val) val

%macro NameEntry 2
%1__  dw %2
db Stringify(%1), 0
%endmacro

salign    equ 1000h   ; Page size in memory
falign    equ 200h    ; Page size in file
imageBase equ 400000h ; Requested load address

BITS 16

section headers start=0
startoffile:

    ; MZ header https://wiki.osdev.org/MZ
    dw  "MZ"                        ; Signature
    dw (dosBlkSize - mzStructSize) % 512  ; Bytes on last page
    dw RoundTo(dosBlkSize, 512) / 512     ; # of 512 byte pages
    dw 0                            ; Relocation items
    dw RoundTo(mzStructSize, 16) / 16 ; Header size in paragraphs
    dw 0                            ; Minimum allocation
    dw 0xffff                       ; Maximum allocation in paragraphs (1M).
    dw 0                            ; Initial SS
    dw 0xb8                         ; Initial SP
    dw 0                            ; Checksum
    dw 0                            ; Initial IP
    dw 0                            ; Initial CS
    dw 0                            ; Relocation table
    dw 0                            ; Overlay
    dq 0                            ; Reserved
    dw 0                            ; OEM identifier
    dw 0                            ; OEM info
    times 20 db 0                   ; Reserved
    dd PEHDR                        ; PE header start

mzStructSize  equ $ - $$ ; aka 64

dosstartcode:   ; Print the error and exit
    push cs
    pop  ds
    mov  dx, dosmsg - dosstartcode
    mov  ah, 0x9
    int  0x21       ; Show string up to '$'
    mov  ax, 4c01h
    int  0x21       ; Exit process with error code 1

    dosmsg db `This program cannot be run in DOS mode.\r\r\n$`

dosBlkSize  equ $ - $$

ALIGN 16

; From https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
PEHDR:
    dd  "PE"            ; signature
    dw  8664h           ; machine x64
    dw  SectionsCount   ; # of sections
    dd  __POSIX_TIME__  ; timedatestamp
    dd  0               ; pointer to symtab - deprecated
    dd  0               ; # symtab entries
    dw  opthdrSize      ; size of optional header
    dw  2h              ; flags: Executable
       
OPTHDR:
    dw  20Bh            ; magic
    db  0               ; maj linker ver
    db  0               ; minor linker ver
    dd  codeSizeS       ; total memory code size
    dd  rdataSizeS      ; total memory init data size
    dd  0               ; total uninit data size
    dd  ENTRY           ; entrypoint RVA   
    dd  section..text.start ; base of code in file
    dq  imageBase       ; image base
    dd  salign          ; section address alignment
    dd  falign          ; section pos alignment
    dw  10              ; major OS version
    dw  0               ; minor OS version
    dw  0               ; major image ver
    dw  1               ; minor image ver
    dw  6               ; major subsystem ver
    dw  2               ; minor subsystem ver
    dd  0               ; win32 version value = 0
    dd  fileSize        ; size of image in memory
    dd  headersSizeF    ; size of DOS stub + PE header + sections
    dd  0               ; checksum
    dw  2               ; subsystem: GUI
    dw  8160h           ; dll characteristics: HighEntropy, Relocatable, NX, TS aware
    dq  100h            ; max stack
    dq  100h            ; min stack
    dq  100h            ; max heap
    dq  100h            ; min heap
    dd  0               ; loader flag

HeaderDirectories:
    dd  HeaderDirectoryCount   ; number of directories
       
    ; Address, Size
    dd  0, 0                        ; Export
    dd ImportsDir, ImportsDirSize   ; Import
    dd 0, 0                         ; Resource
    dd 0, 0                         ; Exception
    dd 0, 0                         ; Certificates
    dd 0, 0                         ; Base Relocation
    dd 0, 0                         ; Debug
    dd 0, 0                         ; Architecture
    dd 0, 0                         ; Global Pointer
    dd 0, 0                         ; Thread Storage
    dd 0, 0                         ; Load Configuration
    dd 0, 0                         ; Bound Import
    dd IATStart, IATSize            ; Import Address Table
    dd 0, 0                         ; Delay Import
    dd 0, 0                         ; COM Descriptor
    dd 0, 0                         ; Reserved

HeaderDirectorySize equ $ - HeaderDirectories
HeaderDirectoryCount equ HeaderDirectorySize / 8

opthdrSize  equ $ - OPTHDR

startOfSections:

    dq  ".text"
    dd  codeSizeS           ; size in memory pages
    dd  ENTRY               ; addr RVA (memory offset)
    dd  codeSize            ; length
    dd  section..text.start ; pos (file offset)
    dd  0           ; relocations addr
    dd  0           ; linenum addr
    dw  0           ; relocations count
    dw  0           ; linenum count
    dd  030000020h  ; flags: Code, Shared, Execute Only

    dq  ".rdata"
    dd  rdataSizeS              ; size in memory pages
    dd  RDATA                   ; addr RVA (memory offset)
    dd  rdataSize               ; length
    dd  section.rdata.start     ; pos (file offset)
    dd  0           ; relocations addr
    dd  0           ; linenum addr
    dw  0           ; relocations count
    dw  0           ; linenum count

    ; Take advantage of the fact that the loader cheats and 
    ; writes imports to readonly pages @ startup
    dd  040000040h  ; flags: Initialized Data, Read Only

SectionsSize equ $ - startOfSections
SectionsCount equ SectionsSize / 40
   
ALIGN 16
headersSizeF equ RoundTo($ - $$, falign)
headersSizeS equ RoundTo($ - $$, salign)

BITS 64

DEFAULT REL ; so we don't have to keep adding imageBase

SECTION .text vstart=headersSizeS align=falign follows=headers

    ENTRY: 
        sub rsp, 28h 
        lea rcx, [cmd]    ; LPCSTR lpCmd
        call [system]

        ; Use the return value from the call to system
        mov ecx, eax
        call [ExitProcess]

codeSize   equ $ - $$
codeSizeS  equ RoundTo(codeSize, salign)

SECTION rdata vstart=headersSizeS+codeSizeS align=falign

RDATA:

IATStart:

; Import Address Table
Kernel32TableA:
    ExitProcess    dq ExitProcess__

MSVCRTTableA:
    system    dq system__

IATSize equ $ - IATStart

ImportsDir:
    dd  Kernel32TableL, 0, 0, kernel32dll, Kernel32TableA
    dd  MSVCRTTableL, 0, 0, MSVCRTdll, MSVCRTTableA

ImportsDirSize   equ $ - ImportsDir

; Kernel32 Import Lookup Table
Kernel32TableL:
    dq ExitProcess__
    dq 0 ; end of table marker

; Name, Hint
NameEntry ExitProcess, 164h

; MSVCRT Import Lookup Table
MSVCRTTableL:
    dq system__
    dq 0 ; end of table marker

; Name, Hint
NameEntry system, 4e0h

kernel32dll   db  "KERNEL32.dll", 0
MSVCRTdll     db  "MSVCRT.dll", 0

; Constant data
cmd     db  "dir > foo.xxx", 0h

ALIGN 16
rdataSize equ $ - RDATA
rdataSizeS equ RoundTo(rdataSize, salign)

fileSize equ RDATA + rdataSizeS

您使用 extern 没有任何意义,masm 中的 extern 意味着您正在调用 API 函数(如 c 中的 printf)而不是可执行文件。 第一条评论是正确的。 在 win32 api 中,从另一个进程启动的进程需要 CreateProcessA 或 CreateProcessW 函数,并且需要设置一个 PROCESS_INFORMATION 结构来传递它。 您可能还必须设置其他结构,我不记得了,然后如果您手动将参数推送到堆栈中,或者只是调用它并让汇编器处理它,您就会调用它。 我建议获取一份 masm32 的副本,这应该很容易,其中或互联网上可能已经有一个示例。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM