FASM 将 Hello World 写入控制台,完全没有包含或依赖项

FASM write Hello World to console with NO includes or dependencies at all

我看过

How to write hello world in assembler under Windows?

Writing hello,world to console in Fasm with DOS

How to write to the console in fasm?

我已经尝试过/看到过类似 this answer

中的 MASM 示例的代码
;---ASM Hello World Win64 MessageBox

extrn MessageBoxA: PROC
extrn ExitProcess: PROC

.data
title db 'Win64', 0
msg db 'Hello World!', 0

.code
main proc
  sub rsp, 28h  
  mov rcx, 0       ; hWnd = HWND_DESKTOP
  lea rdx, msg     ; LPCSTR lpText
  lea r8,  title   ; LPCSTR lpCaption
  mov r9d, 0       ; uType = MB_OK
  call MessageBoxA
  add rsp, 28h  
  mov ecx, eax     ; uExitCode = MessageBox(...)
  call ExitProcess
main endp

End

(我在 windows 64 位 extrn MessageBoxA:PROC 上收到错误“非法指令”,因为 FASM 不理解该 MASM 指令。)

还有这个 FASM 示例 from this question

 ; Example of 64-bit PE program


format PE64 GUI 
entry start 

section '.text' code readable executable 

  start: 
      sub     rsp,8*5         ; reserve stack for API use and make stack dqword aligned 

    mov     r9d,0 
    lea     r8,[_caption] 
    lea     rdx,[_message] 
    mov    rcx,0 
    call    [MessageBoxA] 

    mov     ecx,eax 
    call    [ExitProcess] 

section '.data' data readable writeable 

  _caption db 'Win64 assembly program',0 
  _message db 'Hello World!',0 

section '.idata' import data readable writeable 

  dd 0,0,0,RVA kernel_name,RVA kernel_table 
  dd 0,0,0,RVA user_name,RVA user_table 
  dd 0,0,0,0,0 

  kernel_table: 
    ExitProcess dq RVA _ExitProcess 
    dq 0 
  user_table: 
    MessageBoxA dq RVA _MessageBoxA 
    dq 0 

  kernel_name db 'KERNEL32.DLL',0 
  user_name db 'USER32.DLL',0 

  _ExitProcess dw 0 
    db 'ExitProcess',0 
  _MessageBoxA dw 0 
    db 'MessageBoxA',0

但它会显示一个消息框,并且还具有外部依赖项“kernel32.dll”和“user32.dll”

也试过这个例子from the FASM forum

format pe console


include 'win32ax.inc'

entry main

section '.data!!!' data readable writeable

strHello db 'Hello World !',13,10,0
strPause db 'pause',0

section '.txt' code executable readable

main:
       ; you can use crt functions or windows API.
       cinvoke printf,strHello
       cinvoke system,strPause; or import getc()
       ; or
       ; invoke printf,srtHello
       ; add esp, 4

       ; or use WriteFile and GetStdHandle APIs
       push 0
       call [ExitProcess]
      
section '.blah' import data readable

library kernel32,'kernel32.dll',\
    msvcrt,'msvcrt.dll'    ;; C-Run time from MS. This is always on every windows machine

import kernel32,\
          ExitProcess,'ExitProcess'
import msvcrt,\
          printf,'printf',\
          system,'system'

但这取决于 win32ax.inc 和其他导入

还有

format PE console
include 'win32ax.inc'
.code
start:
        invoke  WriteConsole,<invoke GetStdHandle,STD_OUTPUT_HANDLE>,"Hello World !",13,0
        invoke  Sleep,-1
.end start

但需要“win32ax.inc”导入

没有 win32ax 我能找到的最接近的 from the FASM forum:

format pe64 console
entry start

STD_OUTPUT_HANDLE       = -11

section '.text' code readable executable

start:
        sub     rsp,8*7         ; reserve stack for API use and make stack dqword aligned
        mov     rcx,STD_OUTPUT_HANDLE
        call    [GetStdHandle]
        mov     rcx,rax
        lea     rdx,[message]
        mov     r8d,message_length
        lea     r9,[rsp+4*8]
        mov     qword[rsp+4*8],0
        call    [WriteFile]
        mov     ecx,eax
        call    [ExitProcess]

section '.data' data readable writeable

message         db 'Hello World!',0
message_length  = $ - message

section '.idata' import data readable writeable

        dd      0,0,0,RVA kernel_name,RVA kernel_table
        dd      0,0,0,0,0

kernel_table:
        ExitProcess     dq RVA _ExitProcess
        GetStdHandle    dq RVA _GetStdHandle
        WriteFile       dq RVA _WriteFile
                        dq 0

kernel_name     db 'KERNEL32.DLL',0
user_name       db 'USER32.DLL',0

_ExitProcess    db 0,0,'ExitProcess',0
_GetStdHandle   db 0,0,'GetStdHandle',0
_WriteFile      db 0,0,'WriteFile',0    

但仍然需要 kernel32.dll 和 user32.dll

有什么方法可以在完全不使用任何外部 DLL 的情况下做到这一点?我只知道 fasm 程序本身会执行此操作,并打印到控制台,不是吗?

Any way to do this without any external DLLs at all?

下Windows:绝对不行!

Windows使用了一些方法(可能是syscall)进入操作系统,但是没有正式的入口点

这意味着(不太可能但)在当前 Windows 版本中显示 "Hello world" 消息框的完全相同的程序将在下一个 [=26] 之后做一些完全不同的事情=]更新!

因为 Microsoft 假设每个 Windows 程序仅通过使用与内核版本匹配的 .dll 文件调用 OS,所以他们可以这样做。

我不知道 Windows 10,但是一个较旧的 Windows 版本(我不记得它是 XP、Vista 还是 7)甚至简单地假设 .exe 文件 returns 如果它不使用任何 .dll 文件:在这种情况下程序甚至没有启动!

I know just the program fasm itself does it, and prints to the console

事实并非如此,fasm 也在使用 kernel32 API。

FWIW kernel32 被加载到 Windows 中每个进程的内存 space,因此使用 kernel32 API 没有任何损失或开销。

您可能会喜欢 €ASM 中的这个 Windows 示例,它没有明确提及任何 DLL,也不需要其他外部库。

只需将源保存为“bluej.asm”、assemble和link,将euroasm bluej.asm和运行保存为bluej.exe

然而,如果不使用从默认 Windows 系统库“kernel32.dll”导入的 API 函数,您将无法逃脱。

bluej PROGRAM Format=PE, Entry=Start:
        IMPORT GetStdHandle,WriteFile,ExitProcess
Start:  PUSH -11         ; Param 1: standard output handle identificator.
        CALL GetStdHandle; Return StdOutput handle in EAX.
        PUSH 0           ; Param 5: no overlap.
        PUSH Written     ; Param 4: Address of a variable to store number of written bytes.
        PUSH MsgSize     ; Param 3: Number of bytes to write.
        PUSH Msg         ; Param 2: Address of text.
        PUSH EAX         ; Param 1: Output file handle.
        CALL WriteFile   ; System call.
        PUSH 0           ; Errorlevel.
        CALL ExitProcess ; System call.
Written DD 0
Msg     DB "Hello, world!"
MsgSize EQU $ - Msg
      ENDPROGRAM

对您来说“依赖”是什么?如果您甚至想避免操作系统 DLL,那么您可能就不走运了。您不能仅依赖系统调用编号。

“无依赖” 也可以表示“仅使用现有的 OS DLL”,例如 ntdll、kernel32 等,但不使用第 3 方 DLL可能不存在,例如 特定 版本的 C 运行时。

我想展示的一种方法是从 PEB 中检索函数指针。这是我编写的代码,如果我想要没有导入部分的 shellcode,我会亲自使用。

PebGetProcAddressGetProcAddress类似,只是DLL名和函数名必须是hash,并且DLL必须使用LoadLibrary.[=17=加载]

这可能无法完全回答您的问题,但我希望它能让您更接近您的目标或帮助阅读它的其他人。

PebApi.asm

proc PebGetProcAddress ModuleHash:DWORD, FunctionHash:DWORD
    local   FirstEntry:DWORD
    local   CurrentEntry:DWORD
    local   ModuleBase:DWORD
    local   ExportDirectory:DWORD
    local   NameDirectory:DWORD
    local   NameOrdinalDirectory:DWORD
    local   FunctionCounter:DWORD

    ; Get InMemoryOrderModuleList from PEB
    mov     eax, 3
    shl     eax, 4
    mov     eax, [fs:eax] ; fs:0x30
    mov     eax, [eax + PEB.Ldr]
    mov     eax, [eax + PEB_LDR_DATA.InMemoryOrderModuleList.Flink]
    mov     [FirstEntry], eax
    mov     [CurrentEntry], eax

    ; Find module by hash
.L_module:

    ; Compute hash of case insensitive module name
    xor     edx, edx
    mov     eax, [CurrentEntry]
    movzx   ecx, word[eax + LDR_DATA_TABLE_ENTRY.BaseDllName.Length]
    test    ecx, ecx
    jz      .C_module
    mov     esi, [eax + LDR_DATA_TABLE_ENTRY.BaseDllName.Buffer]
    xor     eax, eax
    cld
.L_module_hash:
    lodsb
    ror     edx, 13
    add     edx, eax
    cmp     al, 'a'
    jl      @f
    sub     edx, 0x20 ; Convert lower case letters to upper case
@@: dec     ecx
    test    ecx, ecx
    jnz     .L_module_hash

    ; Check, if module is found by hash
    cmp     edx, [ModuleHash]
    jne     .C_module

    ; Get module base
    mov     eax, [CurrentEntry]
    mov     eax, [eax + LDR_DATA_TABLE_ENTRY.DllBase]
    mov     [ModuleBase], eax

    ; Get export directory
    mov     eax, [ModuleBase]
    add     eax, [eax + IMAGE_DOS_HEADER.e_lfanew]
    mov     eax, [eax + IMAGE_NT_HEADERS32.OptionalHeader.DataDirectoryExport.VirtualAddress]
    add     eax, [ModuleBase]
    mov     [ExportDirectory], eax

    ; Get name table
    mov     eax, [ExportDirectory]
    mov     eax, [eax + IMAGE_EXPORT_DIRECTORY.AddressOfNames]
    add     eax, [ModuleBase]
    mov     [NameDirectory], eax

    ; Get name ordinal table
    mov     eax, [ExportDirectory]
    mov     eax, [eax + IMAGE_EXPORT_DIRECTORY.AddressOfNameOrdinals]
    add     eax, [ModuleBase]
    mov     [NameOrdinalDirectory], eax

    ; Find function in export directory by hash
    mov     [FunctionCounter], 0
.L_functions:
    mov     eax, [ExportDirectory]
    mov     eax, [eax + IMAGE_EXPORT_DIRECTORY.NumberOfNames]
    cmp     eax, [FunctionCounter]
    je      .E_functions

    ; Compute hash of function name
    xor     edx, edx
    mov     esi, [NameDirectory]
    mov     esi, [esi]
    add     esi, [ModuleBase]
    xor     eax, eax
    cld
.L_function_hash:
    lodsb
    test    al, al
    jz      .E_function_hash
    ror     edx, 13
    add     edx, eax
    jmp     .L_function_hash
.E_function_hash:

    ; Check, if function is found by hash
    cmp     edx, [FunctionHash]
    jne     .C_functions

    ; Return function address
    mov     eax, [ExportDirectory]
    mov     eax, [eax + IMAGE_EXPORT_DIRECTORY.AddressOfFunctions]
    add     eax, [ModuleBase]
    mov     ebx, [NameOrdinalDirectory]
    movzx   ebx, word[ebx]
    lea     eax, [eax + ebx * 4]
    mov     eax, [eax]
    add     eax, [ModuleBase]
    ret

.C_functions:
    add     [NameDirectory], 4
    add     [NameOrdinalDirectory], 2
    inc     [FunctionCounter]
    jmp     .L_functions
.E_functions:

    ; Function not found in module's export table
    xor     eax, eax
    ret

.C_module:
    ; Move to next module, exit loop if CurrentEntry == FirstEntry
    mov     eax, [CurrentEntry]
    mov     eax, [eax + LIST_ENTRY.Flink]
    mov     [CurrentEntry], eax
    cmp     eax, [FirstEntry]
    jne     .L_module

    ; Module not found
    xor     eax, eax
    ret
endp

PebApi.inc

macro pebcall modulehash, functionhash, [arg]
{
    common
    if ~ arg eq
        reverse
        pushd arg
        common
    end if

    stdcall PebGetProcAddress, modulehash, functionhash
    call    eax
}

例子

PEB_User32Dll = 0x63c84283
PEB_MessageBoxW = 0xbc4da2be

; pebcall translates to a call to PebGetProcAddress and the call to the returned function pointer
pebcall PEB_User32Dll, PEB_MessageBoxW, NULL, 'Hello, World!', NULL, MB_OK

如何为模块名称和函数名称生成哈希值

#define ROTR(value, bits) ((DWORD)(value) >> (bits) | (DWORD)(value) << (32 - (bits)))

DWORD ComputeFunctionHash(LPCSTR str)
{
    DWORD hash = 0;

    while (*str)
    {
        hash = ROTR(hash, 13) + *str++;
    }

    return hash;
}

DWORD ComputeModuleNameHash(LPCSTR str, USHORT length)
{
    DWORD hash = 0;

    for (USHORT i = 0; i < length; i++)
    {
        hash = ROTR(hash, 13) + (str[i] >= 'a' ? str[i] - 0x20 : str[i]);
    }

    return hash;
}