如何在汇编程序中运行Windows Shell命令,而无需C库?

发布于 2025-02-06 07:43:20 字数 1290 浏览 1 评论 0原文

我正在学习组装,但是我一直在Linux上学习,并且由于我了解C/C ++,它已经与C/C ++交织在一起。

但是,现在,我想在我的Windows计算机上编写一个程序,该程序调用一个运行shell命令的函数,希望不包括任何库,因为我希望此可执行文件能尽可能小 /strong>。 (一个简单的C ++可执行文件,其中包括cstdlib是32 kb,而我几乎根本不键入任何代码。)

我本质上想编写此程序,但在汇编中:

#include <cstdlib>

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

到目前为止,我已经安装了NASM和MINGW而且我正在尝试使用它们[编辑:我现在正在尝试使用MASM,而不是Visual Studio],但是没有任何可行我遇到的在线资源已经成功地汇编了我试图做的事情。它似乎也不了解语句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

I'm learning assembly, but I've been learning on Linux, and it's been very interwoven with C/C++ since I understand C/C++.

However, now, I want to write a program in assembly on my Windows computer that makes a call to a function that runs a shell command, and hopefully without including any libraries since I want this executable to be as small as possible. (A simple C++ executable I made which included cstdlib is 32 KB while I typed barely any code at all.)

I want to essentially write this program but in assembly:

#include <cstdlib>

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

So far, I have installed NASM and MinGW and I'm trying to use them [Edit: I am now trying to use MASM instead, with Visual Studio], but nothing works and I assume it's because Windows has its own stuff from Microsoft that obviously aren't in Linux, but none of the online resources I've come across have successfully compiled with what I was trying to do. It also doesn't seem to understand the statement extern system, so I think that this might be a C/C++ library thing (from cstdlib).

Also, in some cases I encountered an error where it said the "character constant is too long". I assume it's referring to the string; in the real program, the string to echo is much longer than what's in the example above.

I also had an issue apparently with the .obj file that was generated.

So, basically, all I need is how to turn the above program into assembly for Windows, and how to get NASM and MinGW to turn it into an executable without giving me errors.

Edit: After a few days of researching, I have decided I don't know what I'm doing wrong and I can't figure out what is right.

I've tried Visual Studio now, but once again, it doesn't understand 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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

廻憶裏菂餘溫 2025-02-13 07:43:21

正如我在评论中提到的那样,Windows确实具有系统命令。它位于msvcrt.dll中,因此您需要与msvcrt.lib链接以解决外部参考。

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

32K听起来可能很大,但是如今Windows可以在4K页面上工作,允许它设置页面保护 bits(只读页面,可执行页等)。可执行代码的一个页面,一个用于数据,一个用于常数,进出口,进出口,调试信息,异常信息等的页面。8页似乎并不是当今标准的那样令人毛骨悚然。

但是正如我也提到的那样,有 hacks 如果有一些令人信服的理由,则可以用来减少可执行文件的大小。所以。该示例(使用nasm.exe doit.asm -o doit.exe使用NASM版本2.15.05构建)为1,184字节。当您运行它时,似乎什么都不会发生(没有CMD窗口打开),而且确实很快。但是您会看到foo.xxx会创建并包含来自dir&gt;的输出。 foo.xxx命令嵌入了代码中。

这里的链接器通常为您处理,这里有很多gobbledygook。有趣的位从开始:。 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

正如我在评论中提到的那样,Windows确实具有系统命令。它位于msvcrt.dll中,因此您需要与msvcrt.lib链接以解决外部参考。

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

32K听起来可能很大,但是如今Windows可以在4K页面上工作,允许它设置页面保护 bits(只读页面,可执行页等)。可执行代码的一个页面,一个用于数据,一个用于常数,进出口,进出口,调试信息,异常信息等的页面。8页似乎并不是当今标准的那样令人毛骨悚然。

但是正如我也提到的那样,有 hacks 如果有一些令人信服的理由,则可以用来减少可执行文件的大小。所以。该示例(使用 nasm.exe doit.asm -o doit.exe 使用NASM版本2.15.05构建)为1,184字节。当您运行它时,似乎什么都不会发生(没有CMD窗口打开),而且确实很快。但是您会看到foo.xxx会创建并包含来自 dir&gt;的输出。 foo.xxx 命令嵌入了代码中。

这里的链接器通常为您处理,这里有很多gobbledygook。有趣的位从开始:。 CMD定义在代码末尾附近。

该代码的隐秘性质与小尺寸相结合,可以使用此代码进行恶作剧。我将假设这不是您的意图。

dosBlkSize equ $ - $ ALIGN 16 ; From https://learn.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

As I mentioned in the comments, Windows does have a system command. It's located in MSVCRT.dll, so you'll need to link with msvcrt.lib to resolve the external reference.

The error you are getting from "line 11" is due to incorrectly defining the external, which is odd since you're correctly doing the same for ExitProcess (which is also an external) just a few lines before.

32k may sound big, but Windows works in 4k pages these days, allowing it set page protection bits (read-only pages, executable pages, etc). One page for executable code, one for data, one for constants, imports, exports, debug info, exception info, etc. 8 pages doesn't seem that outrageous by today's standards.

But as I also mentioned, there are hacks one can use to reduce the size of the executable if there's some compelling reason to do so. This sample (built with NASM version 2.15.05 using nasm.exe doit.asm -o doit.exe) is 1,184 bytes. When you run it, nothing seems to happen (no cmd window opens) and it's really quick. But you'll see that foo.xxx gets created and contains the output from the dir > foo.xxx command embedded in the code.

There's a lot of gobbledygook here that the linker normally handles for you. The interesting bits start at ENTRY:. cmd is defined near the end of the code.

The stealthy nature of this code combined with the small size opens the possibility of using this code for mischief. I'm going to assume that's not your intent.

; 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

As I mentioned in the comments, Windows does have a system command. It's located in MSVCRT.dll, so you'll need to link with msvcrt.lib to resolve the external reference.

The error you are getting from "line 11" is due to incorrectly defining the external, which is odd since you're correctly doing the same for ExitProcess (which is also an external) just a few lines before.

32k may sound big, but Windows works in 4k pages these days, allowing it set page protection bits (read-only pages, executable pages, etc). One page for executable code, one for data, one for constants, imports, exports, debug info, exception info, etc. 8 pages doesn't seem that outrageous by today's standards.

But as I also mentioned, there are hacks one can use to reduce the size of the executable if there's some compelling reason to do so. This sample (built with NASM version 2.15.05 using nasm.exe doit.asm -o doit.exe) is 1,184 bytes. When you run it, nothing seems to happen (no cmd window opens) and it's really quick. But you'll see that foo.xxx gets created and contains the output from the dir > foo.xxx command embedded in the code.

There's a lot of gobbledygook here that the linker normally handles for you. The interesting bits start at ENTRY:. cmd is defined near the end of the code.

The stealthy nature of this code combined with the small size opens the possibility of using this code for mischief. I'm going to assume that's not your intent.

dosBlkSize equ $ - $ ALIGN 16 ; From https://learn.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
暮倦 2025-02-13 07:43:21

您对外部的使用没有任何意义,extern在MASM中表示您调用API函数(例如C中的printf)而不是可执行文件。第一个评论是正确的。在Win32 API中,从Anther过程启动的过程中,您需要CreateProcessa或CreateProcessw函数,并且需要设置Process_Information结构来传递它。我不记得您也必须设置其他结构,如果您将其推向堆栈,或者简单地调用它,然后让汇编器处理它。我建议您获得MASM32的副本,这应该使它变得容易,其中可能已经有一个示例或互联网上的样本。

You usage of extern does not make any sense, extern in masm means you are calling an API function (like printf in c) not a executable file. The first comment is correct. In the win32 api a process launched from anther process you need the CreateProcessA or CreateProcessW function and you need to setup a PROCESS_INFORMATION structure to pass it. You might have to set up other structures too, I don't remember, then you call it if you pushed to arguments to the stack manually or simply invoke it and let the assembler handle that. I would recommend getting a copy of masm32, that should make it easy, there is likely already a sample in it or on the internet.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文