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

lp0sw83n  于 2023-06-06  发布在  其他
关注(0)|答案(5)|浏览(326)

我见过
How to write hello world in assembler under Windows?
和/或
Writing hello,world to console in Fasm with DOS
如何在fasm中写入控制台?
我试过/看到过类似于这个答案中的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

(to我在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”导入
最接近我可以找到没有win32 ax 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本身会这样做,并打印到控制台,不是吗?

z5btuh9x

z5btuh9x1#

有没有什么方法可以做到这一点,而不需要任何外部DLL?
在Windows下:绝对不行!
Windows使用一些方法(可能是syscall)进入操作系统,然而,没有官方的入口点。
这意味着在当前Windows版本中显示"Hello world"消息框的完全相同的程序在下一次Windows更新后会做一些完全不同的事情!
因为微软假设每个Windows程序只通过使用与内核版本匹配的.dll文件来调用操作系统,所以他们可以这样做。
我不知道Windows 10,但较旧的Windows版本(我不记得是XP,Vista还是7)甚至简单地假设如果不使用任何.dll文件,.exe文件会立即返回:在这种情况下,程序甚至没有启动!

vkc1a9a2

vkc1a9a22#

  • 我只知道程序fasm自己会这么做,并打印到控制台 *

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

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

3lxsmp7m

3lxsmp7m3#

您可能会喜欢€ASM中的这个Windows示例,它没有明确提到任何DLL,也不需要其他外部库。
只需将源代码保存为“bluej.asm”,用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
l2osamch

l2osamch4#

对你来说什么是“依赖”?如果你想避免甚至操作系统的DLL的,那么你可能不走运。你不能仅仅依靠系统调用的数字。

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

我想展示的一种方法是从PEB中检索函数指针。这是我写的代码,我个人使用,如果我想有没有导入节的shellcode。
PebGetProcAddress的工作方式与GetProcAddress类似,不同之处在于DLL名称和函数名称必须是散列,并且DLL必须使用LoadLibrary加载。
这可能不能完全回答你的问题,但我希望它能让你更接近你的目标,或者帮助其他读到它的人。

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;
}
relj7zay

relj7zay5#

这可能会有所帮助,它读取字符串并打印每个字符,直到NULL:

;in data segment, write this
string dw 'hello world!', 0
;in code segment:
mov si, string
call print

print proc near
start:
 push ax   
 push si
   aloop:
    lodsb
    or al, al
    jz stop
    mov ah, 0Eh
    mov bh, 0
    int 10h
    jmp aloop
stop:
 pop si
 pop ax
 ret
   print endp

相关问题