Не может вернуть дескриптор сокета из WSASocketA

У меня есть файл .cpp, который устанавливает соединение, отлично работает в сборке с g++. У меня также есть 32-битный исполняемый файл, который также устанавливает соединение. Теперь проблема в том, что я хочу использовать 64-битную сборку для этого соединения. Мне удалось дойти до вызова WSASocketA со всеми правильными аргументами (2,1,6,0,0,0), но я просто не могу вернуть дескриптор сокета. Я получаю -1 (0xFFFFFFFF). Затем я заметил, что могу вызвать WSAGetLastError, что я и сделал, и он возвращает 10022. Но мои аргументы верны. Я проанализировал objdump из моего коннектора cpp, у которого точно такие же аргументы, именно в таком порядке. И я полностью осведомлен об использовании правильного соглашения о вызовах для 64-разрядной версии Windows.

Я использую nasm и пробовал как link, так и GoLinker.exe для моих компоновщиков, которые выдают одинаковый точный результат. Я явно использую ws2_32.lib из C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\um\x64 при компоновке, а также 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 вместо 264 или 256, как у моего коннектора cpp. Любые идеи? Я отлаживал это последние несколько дней весь день без перерыва. Моя программа не падает, просто не получает дескриптор сокета. Спасибо.

Использование 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\10\Lib\10.0.19041.0\um\x64\WS2_32.LIB" "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\um\x64\KERNEL32.LIB" "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\ucrt\x64\UCRT.LIB" "legacy_stdio_definitions.lib" /LARGEADDRESSAWARE:NO && connect64.exe

Кстати, lodsq / xchg rax, rsi в 64-битном режиме на самом деле стоит больше байтов (2 + 2 = 4), чем mov rsi, [rsi] (3 байта), вместо того, чтобы быть безубыточным, как в 32-битном режиме. Это не сохраняет старое значение RSI (в RAX), но вы перезаписываете его в следующей инструкции, так что в этом случае это просто попытка быть слишком умным с кодовым гольфом или это запутывание?

Peter Cordes 18.03.2022 05:44

Кроме того, xor rsi, rsi бессмысленно прямо перед mov esi, [r8 + 0x20]; запись 32-битного регистра неявно расширяет нуль до 64-битного регистра. (в отличие от 8 или 16). Вы также можете избежать add r8, rbx, выполнив это в режиме адресации с [r8+rbx+0x20], хотя я думаю, что вы снова используете тот же адрес r8 позже с другим смещением, поэтому имеет смысл суммировать его в один регистр. Я ничего не знаю об этих функциях Windows API или о том, в чем может быть реальная проблема, мне просто было любопытно, выиграл ли ваш lodsq/xchg, поэтому я начал искать подобные вещи в вашем коде.

Peter Cordes 18.03.2022 05:46

Почему ты пишешь это на ассемблере? Как вы уже видели, многое нужно понять из того, что обычно делают за вас языки более высокого уровня. Какую выгоду вы ожидаете от этого (наиболее трудного) пути?

David Wohlferd 18.03.2022 06:40
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
56
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Windows ABI имеет три требования, относящиеся к этому коду:

  • rsp должен быть выровнен по 16 байтам перед вызовом.
  • В верхней части стека перед вызовом должно быть 32 байта свободного места, которое может свободно использоваться вызываемой функцией.
  • Первые 4 параметра функции находятся в rcx, rdx, r8 и r9, а остальные параметры находятся в стеке, начиная с rsp+0x20.

Когда вызывается процедура, вызов помещает 8-байтовый адрес возврата в стек. Таким образом, каждая функция должна скорректировать стек на нечетное число, кратное 8, чтобы выровнять его до 16-байтовой границы.

Показанный код помещает последние два аргумента в стек, а затем после этого вычитает 0x30 из указателя стека, поэтому два переданных аргумента находятся не в rsp+0x20, где они должны быть. Вместо этого следует вычесть 0x20.

Лучшее решение, используемое компиляторами, состоит в том, чтобы вычесть 0x38 из rsp в начале функции и больше не изменять rsp внутри функции. Инициализируйте два параметра с помощью mov qword [rsp+0x20], 0; mov qword [rsp+0x28], 0 вместо толчка.

я сделал некоторые правки. я читал, что для функций требуется 32-байтовое теневое пространство стека. любые дальнейшие нажатия имеют размер 8 байт каждый, но всегда должны быть выровнены по 16 байтам. некоторые функции требуют 0x28, что составляет 40 байт. больше или меньше, это не сработает. он все еще не работает должным образом, но выдает ошибки, и все функции работают, кроме WSASocket.

yeahbuddeh 18.03.2022 07:38

я попытался переместить сабвуфер rsp, 0x28 выше 2 нажатий, не имеет значения. это не имеет значения, потому что вычитание из стека не меняет эти 2 аргумента. добавление в стек до того, как вызов очистил бы их

yeahbuddeh 18.03.2022 07:50

Это абсолютно имеет значение. Вы должны вычесть 0x20 после нажатия. Ничто другое не оставит эти два аргумента в нужном месте, чтобы функция могла их найти. Я добавил лучшее решение к ответу.

prl 18.03.2022 09:59

Причина, по которой проблема возникает только с WSASocket, заключается в том, что это единственная вызываемая вами функция, имеющая более 4 параметров.

prl 18.03.2022 10:04

Это сработало, но я не понимаю, почему мне нужно вычитать 0x20 после нажатия, когда на самом деле я должен вычесть его ДО (и я только что попытался оставить 2 нажатия 0, и это все еще не работает). Это работает только тогда, когда я использую метод компилятора, как вы сказали. Кроме того, по какой-то причине я понял, что если я не выровню его как 0x18, 0x28, 0x38, ничего не будет напечатано. И работает только 0x38 но почему? Его 56 байт, но стек всего 48 байт. обычный 32 и 2 push = 32 + 16 = 48, но он не будет работать с 0x30

yeahbuddeh 18.03.2022 17:51

4-й и 5-й аргументы должны находиться в rsp+0x20 перед вызовом, потому что именно там их ищет вызываемая процедура. Он не может просто волшебным образом найти их, если они не находятся в нужном месте в стеке.

prl 18.03.2022 20:08

Перед вызовом указатель стека должен быть выровнен по 16 байтам. Когда вызывается процедура, вызов помещает 8-байтовый адрес возврата в стек. Таким образом, ваша функция должна настроить стек на странный, кратный 8, чтобы выровнять его до 16-байтовой границы. 0x38, 0x48, 0x58, ... все будет работать. 0x28 или меньше не выделяет достаточно места.

prl 18.03.2022 20:10

Другие вопросы по теме