不能 return 来自 WSASocketA 的套接字描述符

cannot return socket descriptor from WSASocketA

我有一个建立连接的 .cpp 文件,与 g++ 完美组装。我还有一个 32 位 exe 也可以建立连接。现在的问题是我想使用 64 位汇编来建立这个连接。我能够使用所有正确的参数 (2,1,6,0,0,0) 调用 WSASocketA,但我根本无法 return 套接字描述符。我得到 -1 (0xFFFFFFFF)。然后我注意到我可以调用我所做的 WSAGetLastError,它 returns 10022。但我的论点是正确的。我按顺序分析了来自我的 cpp 连接器的 objdump,它具有完全相同的参数。我完全知道在 windows.

中使用正确的 64 位调用约定

我正在使用 nasm 并为我的 link 用户尝试了 link 和 GoLinker.exe,它们产生完全相同的输出。当 linking 以及 kernel32.lib 和 ucrt.lib 以及 64 位版本。有什么原因我无法获得套接字描述符。我很确定我的代码是正确的:

BITS 64

SECTION .data

fmtd:
db "%d", 0x0a,0

fmts:
db "%s", 0x0a,0


SECTION .bss
;pStru: resq    1         ; This is a pointer for a dynamically created structure - malloc style
pStru:  resb    0x190     ; Shadow space for a statically created structure

SECTION .text
extern printf
extern malloc
global main
main:

;push rbp
;mov rbp, rsp

;sub   rsp, 0x28                 ; 40 bytes of shadow space
;and   rsp, 0FFFFFFFFFFFFFFF0h   ; Align the stack to a multiple of 16 bytes

; Parse PEB and find kernel32

xor rcx, rcx             ; RCX = 0
mov rax, [gs:rcx + 0x60] ; RAX = PEB
mov rax, [rax + 0x18]    ; RAX = PEB->Ldr
mov rsi, [rax + 0x20]    ; RSI = PEB->Ldr.InMemOrder
lodsq                    ; RAX = Second module
xchg rax, rsi            ; RAX = RSI, RSI = RAX
lodsq                    ; RAX = Third(kernel32)
mov rbx, [rax + 0x20]    ; RBX = Base address

; Parse kernel32 PE

xor r8, r8                 ; Clear r8
mov r8d, [rbx + 0x3c]      ; R8D = DOS->e_lfanew offset
mov rdx, r8                ; RDX = DOS->e_lfanew
add rdx, rbx               ; RDX = PE Header
mov r8d, [rdx + 0x88]      ; R8D = Offset export table
add r8, rbx                ; R8 = Export table
xor rsi, rsi               ; Clear RSI
mov esi, [r8 + 0x20]       ; RSI = Offset namestable
add rsi, rbx               ; RSI = Names table
xor rcx, rcx               ; RCX = 0
mov r9, 0x41636f7250746547 ; GetProcA

; Loop through exported functions and find GetProcAddress

Get_Function:

inc rcx                    ; Increment the ordinal
xor rax, rax               ; RAX = 0
mov eax, [rsi + rcx * 4]   ; Get name offset
add rax, rbx               ; Get function name
cmp QWORD [rax], r9        ; GetProcA ?
jnz Get_Function
xor rsi, rsi               ; RSI = 0
mov esi, [r8 + 0x24]       ; ESI = Offset ordinals
add rsi, rbx               ; RSI = Ordinals table
mov cx, [rsi + rcx * 2]    ; Number of function
xor rsi, rsi               ; RSI = 0
mov esi, [r8 + 0x1c]       ; Offset address table
add rsi, rbx               ; ESI = Address table
xor rdx, rdx               ; RDX = 0
mov edx, [rsi + rcx * 4]   ; EDX = Pointer(offset)
add rdx, rbx               ; RDX = GetProcAddress
mov rdi, rdx               ; Save GetProcAddress in RDI

; Use GetProcAddress to find the address of LoadLibrary

mov rcx, 0x41797261          ; aryA
push rcx                     ; Push on the stack
mov rcx, 0x7262694c64616f4c  ; LoadLibr
push rcx                     ; Push on stack
mov rdx, rsp                 ; LoadLibraryA
mov rcx, rbx                 ; kernel32.dll base address (rbx never changes so we could use it later for CreateProcessA)
sub rsp, 0x20                ; Allocate stack space for function call
call rdi                     ; Call GetProcAddress
add rsp, 0x20                ; Cleanup allocated stack space
mov rsi, rax                 ; LoadLibrary saved in RSI


;getws2_32:
mov rcx, 0x6c6c                 ; ll
push rcx                        ; Push on the stack
mov rcx, 0x642e32335f327377     ; d.32_2sw
push rcx                        ; Push on the stack
mov rcx, rsp                    ; ws2_32.dll
sub rsp, 0x20                   ; Allocate stack space for function call
call rsi                        ; call Loadlibrary (stored in rsi) and find ws2_32.dll
add rsp, 0x20                   ; Cleanup allocated stack space
mov r15, rax                    ; base address of ws2_32.dll saved in local variable r15 (winsock handle)


;getWSAStartup:
mov rcx, 0x7075                  ; pu
push rcx                         ; Push on the stack
mov rcx, 0x7472617453415357      ; tratSASW
push rcx                         ;Push on the stack
mov rdx, rsp                     ; copy WSAStartup from stack to 2nd argument (rdx is the 2nd arg)
mov rcx, r15                     ; winsock handler
sub rsp, 0x20                   ; Allocate stack space for function call
call rdi                         ; GetProcAddress(ws2_32.dll, WSAStartup)
add rsp, 0x20                   ; Cleanup allocated stack space
mov r14, rax                     ; ws2_32.WSAStartup saved in r14


;callWSAStartUp:
; malloc style just uncomment resq 0x190 for pStru in .bss
;mov     rcx, 0x198                    ; size of the structure
;call    malloc                     ; get the memory allocated
;mov     qword [ pStru ], rax       ; store the address in the pointer
;xor rdx, rdx
;lea rdx, [ pStru ] ; pointer to our WSAData structure
;xor rcx, rcx
;mov cx,  0x202
;call r14


; stack style
;xor rcx, rcx
;mov cx, 0x190         ; 0x190 works only when 0x28 bytes are subtracted, no more, no less!!!
;sub rsp,rcx
;lea rdx,[rsp]
;xor rcx, rcx
;mov cx,0x202
;sub rsp, 0x28
;call r14              ; call WSAStartup(MAKEWORD(2, 2), wsadata_pointer)
;add rsp, 0x28
;add rsp, 0x190


; static (.bss) style just uncomment resb 0x190 for pStru in .bss
xor rdx, rdx
lea rdx, [ pStru ] ; pointer to our WSAData structure
xor rcx, rcx
mov cx, 0x202          ; version 2,2 = 514 in decimal = 0x202 in hex, must be a word (2 bytes so only register cx is used)
sub rsp, 0x28
call r14
add rsp, 0x28

; print return value from WSAStartup (0 if no errors)
mov rdx, rax
mov rcx, fmtd
sub rsp, 0x20
call printf
add rsp, 0x20


;getWSASocketA:
xor rdx, rdx
xor rcx, rcx
mov rcx, 0x4174                  ; 'At'  original
push rcx                         ; push on stack
mov rcx, 0x656b636f53415357      ; 'ekcoSASW'
push rcx                         ; push on stack
mov rdx, rsp                     ; copy string of WSASocketA contents from stack to rdx (2nd arg for GetProcAddress)
mov rcx, r15                     ; socket handler ws2_32.dll
sub rsp, 0x30
call rdi                         ; GetProcAddress(ws2_32.dll, WSASocketA)
add rsp, 0x30                   ; Cleanup allocated stack space: standard is 32 bytes but 2 pushes = 16 more = 48 = 0x30
mov r13, rax                     ; save ws2_32.WSASocketA to r13


;callWSASocketA:
xor r9, r9                      ; lpProtocolInfo=NULL (uses itself from above: NULL)
push r9                         ; dwFlags=NULL
push r9                         ; g=NULL
xor r8, r8
mov r8, 0x6                     ; protocol=6
xor rdx, rdx
mov rdx, 0x1                    ; type=1
xor rcx, rcx
mov rcx, 0x2                    ; af=2
sub rsp, 0x28
call r13                        ; call WSASocketA
add rsp, 0x28                  ; Cleanup allocated stack space = 48 bytes = 0x30
mov r14, rax                    ; save socket descriptor of WSASocketA to r14

; print return value from WSASocketA (should be a socket descriptor)
xor rdx, rdx
mov rdx, r14
mov rcx, fmtd
sub rsp, 0x20
call printf
add rsp, 0x20

;getWSAGetLastError:
xor rcx, rcx
mov rcx, 0x726f7272457473        ; rorrEts
push rcx
mov rcx, 0x614c746547415357      ; aLteGSASW
push rcx                         ;Push on the stack
mov rdx, rsp                     ; copy WSAGetLastError from stack to 2nd argument (rdx is the 2nd arg)
mov rcx, r15                     ; winsock handler
sub rsp, 0x30                   ; Allocate stack space for function call
call rdi                         ; GetProcAddress(ws2_32.dll, WSAStartup)
add rsp, 0x30                   ; Cleanup allocated stack space
mov r11, rax                     ; ws2_32.WSAGetLastError saved in r11

;callWSAGetLastError:
call r11

; print return value from WSAGetLastError (which should be an error number if WSASocketA failed)
xor rdx, rdx
mov rdx, rax
mov rcx, fmtd
sub rsp, 0x20
call printf
add rsp, 0x20

完成后,rax 有一个 -1 而不是像我的 cpp 连接器那样的 264 或 256。有任何想法吗?我最近几天整天不停地调试这个。我的程序没有崩溃,它只是没有获得套接字描述符。谢谢

使用GoLink.exe:

nasm -f win64 connect64.s
c:\Golink\GoLink.exe /console /entry main kernel32.dll msvcrt.dll ws2_32.dll connect64.obj /fo connect64.exe && connect64.exe

nasm -f win64 connect64.s && link connect64.obj /SUBSYSTEM:CONSOLE /OUT:connect64.exe /ENTRY:main "C:\Program Files (x86)\Windows Kits\Lib.0.19041.0\um\x64\WS2_32.LIB" "C:\Program Files (x86)\Windows Kits\Lib.0.19041.0\um\x64\KERNEL32.LIB" "C:\Program Files (x86)\Windows Kits\Lib.0.19041.0\ucrt\x64\UCRT.LIB" "legacy_stdio_definitions.lib" /LARGEADDRESSAWARE:NO && connect64.exe

Windows ABI 有三个与此代码相关的要求:

  • rsp 在调用之前必须是 16 字节对齐的。
  • 调用前栈顶必须有32字节空闲space,可供被调用函数自由使用
  • 前4个函数参数在rcx、rdx、r8、r9中,其余参数在从rsp+0x20开始的栈中。

调用过程时,调用会将 8 字节 return 地址压入堆栈。所以每个函数都必须将堆栈调整为 8 的奇数倍,以将其重新对齐到 16 字节边界。

显示的代码将最后两个参数压入堆栈,然后从堆栈指针中减去 0x30,因此压入的两个参数不在它们需要的 rsp+0x20 处。它应该减去 0x20。

更好的解决方案(编译器使用的解决方案)是在函数开头从 rsp 中减去 0x38,并且在函数内不再更改 rsp。使用mov qword [rsp+0x20], 0初始化两个参数; mov qword [rsp+0x28], 0 而不是推送。