News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

API NetShareEnum

Started by Fraile, February 02, 2024, 11:51:55 PM

Previous topic - Next topic

Fraile

I am trying to retrieve shared resources using the NetShareEnum API, but I am not sure where I am going wrong. Please help.

Code:

.Const


.Data?


.Data
  szServerName DB "82.223.12.188", 0
  dwLevel      DD 2
  pBuf         DD 0
  dwPrefMaxLen DD MAX_PREFERRED_LENGTH
  dwEntriesRead DD 0
  dwTotalEntries DD 0
  szOutput     DB 256 Dup(0)

  hInst            HINSTANCE   NULL


.Code

start:
   Invoke GetModuleHandle, NULL
   Mov hInst, Eax
   Invoke GetCommandLine

   invoke NetShareEnum, addr szServerName, dwLevel, addr pBuf, dwPrefMaxLen, addr dwEntriesRead, addr dwTotalEntries, 0
  .if eax == NERR_Success
    ; El resultado fue exitoso, pBuf contiene la información sobre los recursos compartidos
    mov eax, pBuf
    mov ecx, dwEntriesRead

    .repeat
        ; Obtener el puntero al nombre del recurso compartido (shi2_netname)
        mov esi, [eax].SHARE_INFO_2.shi2_netname
        ; Copiar el nombre del recurso compartido en szOutput
        mov edi, offset szOutput
        .repeat
            mov al, [esi]
            mov [edi], al
            inc esi
            inc edi
        .until al == 0  ; Termina cuando se encuentra el carácter nulo
        ; Imprimir el nombre del recurso compartido
        invoke StdOut, addr szOutput

        ; Siguiente entrada
        add eax, SIZEOF SHARE_INFO_2
        dec ecx
    .until ecx == 0

    ; Liberar la memoria asignada para la información
    invoke NetApiBufferFree, pBuf
  .else
    ; Manejar el error, por ejemplo, mostrar un mensaje
    invoke MessageBox, 0, addr szServerName, addr szServerName, MB_OK
  .EndIf

  Invoke ExitProcess, 0

StdOut PROC lpOutput :DWORD
    LOCAL hConsoleOut: HANDLE
    LOCAL bytesWritten: DWORD

    ; Obtener el identificador de la consola de salida
    invoke GetStdHandle, STD_OUTPUT_HANDLE
    mov hConsoleOut, eax

    ; Escribir la cadena en la consola
    invoke WriteConsole, hConsoleOut, lpOutput, LENGTHOF szOutput, addr bytesWritten, 0

    ret
StdOut EndP


End start

jj2007

You might get help easier if
- you post complete code, with all headers and includes
- you tell us what GetLastError reported
- you used English names and comments.

fearless

The NetShareEnum says that servername is:


QuotePointer to a string that specifies the DNS or NetBIOS name of the remote server on which the function is to execute. If this parameter is NULL, the local computer is used.

https://learn.microsoft.com/en-us/windows/win32/api/lmshare/nf-lmshare-netshareenum

I guess you could try prefixing the ip address with \\ and terminating it with \ - but i'm unsure if that will work.

Example: szServerName  db  "\\192.168.1.1\",0

That assumes you had a windows pc/server at that address of course.

Fraile

The headers is:

include \masm32\include\windows.inc
include \masm32\include\netapi32.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc

includelib \masm32\lib\netapi32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib

fearless:

I have tried it with '\192.168.1.1\' but nothing comes up; it always goes to the error control 'else'.



_japheth

Quote from: Fraile on February 03, 2024, 12:24:38 AMI have tried it with '\192.168.1.1\' but nothing comes up; it always goes to the error control 'else'.

Well, why don't you display the error code returned in EAX? That may give you a hint what's wrong.

Your stdout proc is also a bit weird. It's probably better to calc the size of the string that you actually want to display ( by using lstrlen, for example ) than to display always 256 characters, as you do now.

The NetAPI functions, IIRC, usually want string arguments as wide strings. That might help...
Dummheit, gepaart mit Dreistigkeit - eine furchtbare Macht.

jj2007

Quote from: Fraile on February 03, 2024, 12:24:38 AMThe headers is:

We are getting nearer :thumbsup:

Attached a slightly modified version of your code. It outputs print$ but stops then. I leave it to you to find out why dwEntriesRead is 3 but only one entry is present.

Please study the concept of volatile registers in Assembly, and modify your StdOut as follows:
    ; Escribir la cadena en la consola
    mov ecx, len(lpOutput)
    invoke WriteConsole, hConsoleOut, lpOutput, ecx, addr bytesWritten, 0

Quote from: fearless on February 03, 2024, 12:17:46 AMszServerName  db  "\\192.168.1.1\",0

Error 5, access denied; RPC_S_INVALID_BINDING for single backslash

NoCforMe

Quote from: jj2007 on February 04, 2024, 01:39:10 AMI leave it to you to find out why dwEntriesRead is 3 but only one entry is present.
Hint: learn about volatile registers.

(JJ, hope I haven't given too much away here ...)
Assembly language programming should be fun. That's why I do it.

jj2007

Quote from: NoCforMe on February 04, 2024, 11:37:31 AMlearn about volatile registers.

That was the reason for some problems, but unfortunately it's more complicated. Fire up Olly and look under the hood. I can make it print three entries, but it's totally different from what the OP perceived as the documented procedure.

Attached a version that works.

Fraile

#8
hi, I can already get it to return the shared resources it finds on the IP, but I'm not clear on how to display the name of the resource...

PasarADecimal    Proto :DWord, :BOOLEAN

.Const

.Data?
  dwEntriesRead dd ?
  dwTotalEntries dd ?


.Data
  ;szRemoteMachine DB "\\82.223.12.188", 0 ; Cambia la dirección IP por la IP remota que desees
  szRemoteMachine DW '\', '\', '8', '2', '.', '2', '2', '3', '.', '1', '2', '.', '1', '8', '8', 0
  ;szRemoteMachine DW '\', '\', '4', '6', '.', '2', '4', '.', '4', '0', '.', '2', '2', '5', 0

  szOutput DB 256 Dup(0)
  pBuf DD 0
  resume_handle DD 0


hInst        HINSTANCE    NULL

  Titulo              DB "FraiSMBShare.", 0
  szServerName          DB "Recurso.", 0
  tempbuffer              DB 10 Dup(NULL)                ; Buffer temporal para la rutina de paso a numeros decimales.

  ;ShareStruc SHARE_INFO_2 10 Dup({0})
  ShareStruc SHARE_INFO_2 <>

  totalSharesText DB "Numero total de recursos compartidos: ", 0


.Code

start:
    Invoke GetModuleHandle, NULL
    Mov hInst, Eax
    ;=====================

  ; Cargar la biblioteca dinámica wnetapi32.dll
  Invoke LoadLibrary, TextStr("netapi32.dll")
  .If Eax

    Mov Edi, Eax

    ; Obtener el puntero a la función NetShareEnum
      Invoke GetProcAddress, Edi, TextStr("NetApiBufferAllocate")

    .If Eax

        Mov Esi, Eax

        Lea Eax, pBuf
        Push Eax
        Mov Eax, SizeOf ShareStruc
        Push Eax

        Call Esi

        .If Eax == NERR_Success

              ; Obtener el puntero a la función NetShareEnum
            Invoke GetProcAddress, Edi, TextStr("NetShareEnum")

            .If Eax

                Mov Esi, Eax

                Lea Eax, resume_handle
                Push Eax
                Lea Eax, dwTotalEntries
                Push Eax
                Lea Eax, dwEntriesRead
                Push Eax
                Mov Eax, SizeOf SHARE_INFO_2
                Push Eax
                Lea Eax, ShareStruc
                Push Eax
                Push 2
                Lea Eax, szRemoteMachine
                Push Eax


                Call Esi

                .If Eax == NERR_Success

                    Invoke StdOut, Addr totalSharesText
                    Invoke PasarADecimal, dwTotalEntries, FALSE
                    Invoke StdOut, Addr tempbuffer

                    ;Lea Esi, [ShareStruc]
                    ;Invoke MessageBox, NULL, [Esi].SHARE_INFO_2.shi2_netname, Addr szServerName, MB_OK
       
                    ;Invoke StdOut, Eax

;                    Mov Eax, ShareStruc [1].shi2_netname
;                    Mov Ebx, Offset szOutput
;                    Invoke lstrcpy, Ebx, Eax
;
;                    Invoke EscribirVisorSucesos, Offset szOutput

                    ;Invoke MessageBox, 0, [pBuf].SHARE_INFO_2
  • .shi2_netname, NULL, NULL

                    ; Imprimir en la consola el nombre del recurso
                    ;Invoke StdOut, Addr szOutput

                .Else
                    Invoke PasarADecimal, Eax, TRUE
                .EndIf
            .EndIf

        .EndIf
    .EndIf
  .EndIf

    ;=====================
    Invoke ExitProcess, 0

PasarADecimal Proc Valor:DWord, EscribirVisor:BOOLEAN

        Push Esi
        Push Edi

        Mov Esi, Valor
        Mov Edi, Offset tempbuffer
        Invoke dwtoa, Esi, Edi

        Pop Edi
        Pop Esi

        ;Invoke MessageBox, 0, Offset tempbuffer, Offset Titulo, MB_OK
        .If EscribirVisor
            Invoke EscribirVisorSucesos, Offset tempbuffer
        .EndIf

        Ret

PasarADecimal EndP

EscribirVisorSucesos Proc Mensaje:Ptr CHAR

;    Local Tamano:DWord
    Local HandleEventos:DWord

    Local MemoriaMsg:PCHAR
    Local cbBuffer:DWord

    Invoke RegisterEventSource, NULL, Offset Titulo
    Mov HandleEventos, Eax

    invoke lstrlen,Mensaje
    lea eax,[eax+1]
    mov cbBuffer,eax
   
    Invoke GlobalAlloc, GPTR, cbBuffer
    Mov MemoriaMsg, eax

    Invoke RtlMoveMemory, MemoriaMsg, Mensaje, cbBuffer

    Invoke ReportEvent, HandleEventos, EVENTLOG_INFORMATION_TYPE, 0, 1, NULL, 1, DWORD ptr cbBuffer, Addr MemoriaMsg, MemoriaMsg

    Invoke GlobalFree, MemoriaMsg


    Invoke DeregisterEventSource, HandleEventos


    Ret

EscribirVisorSucesos EndP

; Función para imprimir en la consola
StdOut PROC lpOutput :DWORD
    ; Obtener un puntero a la consola de salida estándar
    invoke GetStdHandle, STD_OUTPUT_HANDLE
    ; Verificar si GetStdHandle devolvió un identificador de archivo válido
    .if eax != INVALID_HANDLE_VALUE
        ; Escribir la cadena en la consola
        Push Eax
            Invoke lstrlen, lpOutput
            Mov Ebx, Eax
        Pop Eax
        Invoke WriteConsole, Eax, lpOutput, Ebx, 0, 0
    .endif
    ret
StdOut EndP

End start



NoCforMe

Hint: To make your code easier for everyone here to read, use the [code] -- [/code] tags (there's a button for this when you compose your post) which puts the text in a nice scrollable box. Not a requirement, of course, but it does make it easier for us all.

Example:
LMinsert PROC lminfo:DWORD, element:DWORD, index:DWORD

CMP _InitFlag, TRUE
JE @F
MOV EAX, $LMerr_notInit
JMP exit99

@@: PUSH EBX
PUSH ESI
PUSH EDI
MOV EBX, lminfo

; First check if there's enough room for a new element:
MOV EAX, [EBX].LMINFO.maxNumElements
CMP EAX, [EBX].LMINFO.elementCount
JE ovflow

; Check for the easy case, adding to the end:
CMP index, $LM_InsAtEnd
JE add2end

; If index > # elements, out of range:
MOV EAX, index
CMP EAX, [EBX].LMINFO.elementCount
JA oor

; If index = # elements, add to end:
JE add2end

Assembly language programming should be fun. That's why I do it.

Fraile

Thank you very much, NoCforMe, I will keep that in mind."



Fraile

I've done it!!! This afternoon I'll publish a POC, in case it might be of interest to the community.

Vortex

Hi Fraile,

Kindly, could you pleae enclose you code between coding tags? Easier to read and follow. Thanks.

Fraile

Hello everyone, sorry, but I don't know how to include my encoding tags. Please, if you could provide me with an example, I would appreciate it and I would do it like this in the future. Thank.

Fraile

Here is the ASM code of the project, it still needs some tweaking... But well, you can get an idea of how it works. I work with EASY CODE by Ramon Salas, and compile with MASM32. Once you compile it, you have to pass the IP address from which you want to retrieve the shared resources as an argument. Example: program.exe \192.168.1.1 The program will display the name of the resource, the type, and the comment. I am improving it... to extract more information. Well, I hope you enjoy it. Thanks to everyone for your responses. Best regards.


AsigText Macro Name, Text:VarArg
    Local lbl
    Jmp lbl
        Name DB Text, 0
lbl:

EndM

.Const

bufferSize Equ 256

.Data?

.Data


   
    hInst        HINSTANCE    NULL

    level          DD 1                  ; Nivel de detalle para la enumeración
    buf            DD 0                  ; Puntero al buffer de salida
    entriesRead    dd 0                  ; Número de entradas leídas
    totalEntries    dd 0                  ; Número total de entradas

    fmtAccessDeniedError db "Error de acceso denegado.", 0
    fmtNotEnoughMemoryError db "Error: No hay suficiente memoria disponible.", 0
    tempbuffer      DB 10 Dup(NULL)      ; Buffer temporal para la rutina de paso a numeros decimales.
    HandleConsola    DD 0
    CarroDeRetorno  DB 13 ; Código ASCII para retorno de carro
    NuevaLinea      DB 10 ; Código ASCII para nueva línea

    serverName  DW bufferSize Dup(?)
    format      DB 'Argumentos %d = %s', 13, 10, 0
    buffer      DB 256 Dup(?)
    TamanoNameRecurso DD 0
    shi1_netnameAux DW 20 Dup(' '), 0



.Code

start:
    Invoke GetModuleHandle, NULL
    Mov hInst, Eax

    ; Obtener el identificador de la consola estándar
    Invoke GetStdHandle, STD_OUTPUT_HANDLE
    Mov HandleConsola, Eax  ; edx ahora contiene el identificador de la consola

    ;=====================

    AsigText Cabecera0, "----------------------------------"
    Invoke WriteConsoleA, HandleConsola, Addr Cabecera0, 34, 0, 0
    Invoke ImprimirSaltoDeLinea

    AsigText Cabecera1, "FraiSMBShareEnum by Fraile - 2024."
    Invoke WriteConsoleA, HandleConsola, Addr Cabecera1, 34, 0, 0
    Invoke ImprimirSaltoDeLinea

    AsigText Cabecera2, "----------------------------------"
    Invoke WriteConsoleA, HandleConsola, Addr Cabecera2, 34, 0, 0
    Invoke ImprimirSaltoDeLinea
    Invoke ImprimirSaltoDeLinea




  ; Cargar la biblioteca dinámica wnetapi32.dll
  Invoke LoadLibrary, TextStr("netapi32.dll")

  .If Eax

        Mov Edi, Eax

        ; Obtener el puntero a la función NetShareEnum
        Invoke GetProcAddress, Edi, TextStr("NetShareEnum")

        .If Eax

            Mov Esi, Eax

            ; Calcular el tamaño del buffer necesario
            ; Reservar memoria para el buffer
            Invoke GlobalAlloc, GMEM_ZEROINIT, 4096 ; Puedes ajustar el tamaño del buffer según tus necesidades
            Mov buf, Eax

            Push Esi
            Invoke ArmarArgumentos
            Pop Esi
               

            Push 0
            Lea Eax, totalEntries
            Push Eax
            Lea Eax, entriesRead
            Push Eax
            Mov Eax, MAX_PREFERRED_LENGTH
            Push Eax
            Lea Eax, buf
            Push Eax
            Push level
            Lea Eax, serverName
            Push Eax

            Call Esi

            Test Eax, Eax
            Jnz handle_error

            ; Mostramos el numero de recursos detectados.
            Invoke ImprimirSaltoDeLinea
            AsigText Cabecera3, "Total de recursos detectados: "
            Invoke WriteConsoleA, HandleConsola, Addr Cabecera3, 30, 0, 0
            Invoke PasarADecimal, totalEntries
            Invoke WriteConsoleA, HandleConsola, Addr tempbuffer, 4, 0, 0
            Invoke ImprimirSaltoDeLinea
            Invoke ImprimirSaltoDeLinea

            AsigText Cabecera4, "Nombre Recurso        Tipo"
            Invoke WriteConsoleA, HandleConsola, Addr Cabecera4, 26, 0, 0
            Invoke ImprimirSaltoDeLinea

            AsigText Cabecera5, "------------------------------------------"
            Invoke WriteConsoleA, HandleConsola, Addr Cabecera5, 42, 0, 0
            Invoke ImprimirSaltoDeLinea


            ; Pintar los recursos compartidos.
            ; ********************************

          Mov Esi, buf ; esi apunta al búfer de datos
          Mov Ecx, entriesRead ; Número de recursos compartidos
         
          ; Bucle para leer y procesar cada estructura SHARE_INFO_1 en el búfer
          .repeat
            ; Acceder a los miembros de la estructura SHARE_INFO_1
            Mov Edx, [Esi].SHARE_INFO_1.shi1_netname ; Nombre del recurso compartido (Unicode)
            ; Aquí puedes hacer lo que necesites con la información
            Lea Ebx, [Edx]
            Push Ebx
            Invoke UniStrLen, Ebx
            Mov TamanoNameRecurso, Eax
            Pop Ebx

            ; Ajustamos el nombre del recurso a 20 caracteres.

            .If TamanoNameRecurso < 20

                Push Ecx

                Mov Eax, 20
                Mov Edx, TamanoNameRecurso

                Sub Eax, Edx

                Push Ebx
                Push Eax

                ; Limpiar el buffer
                Mov Edi, Offset shi1_netnameAux    ; Puntero al inicio del buffer
                Mov Ecx, LengthOf shi1_netnameAux  ; Longitud del buffer (número de elementos)
                Mov Ax, ' '  ; Valor a escribir en el buffer (0 para limpiar)
                Rep Stosw  ; Llena el buffer con el valor en ax (0)

                Pop Eax
                Pop Ebx

                Invoke lstrcpynW, Addr shi1_netnameAux, Ebx, Eax

                Pop Ecx

            .EndIf


            Invoke WriteConsoleW, HandleConsola, Addr shi1_netnameAux, 20, 0, 0
   
            ; Tipo de Recurso
            Mov Edx, [Esi].SHARE_INFO_1.shi1_type ; Tipo de recurso.
   
            .If Edx == STYPE_DISKTREE
                AsigText TipoRecurso, "Disco."
                Invoke WriteConsoleA, HandleConsola, Addr TipoRecurso, 6, 0, 0
            .Else
                .If Edx == STYPE_IPC
                    AsigText TipoRecurso1, "IPC."
                    Invoke WriteConsoleA, HandleConsola, Addr TipoRecurso1, 4, 0, 0
                    .If Edx == STYPE_DEVICE
                        AsigText TipoRecurso2, "Dispositivo Comunicacion."
                        Invoke WriteConsoleA, HandleConsola, Addr TipoRecurso2, 25, 0, 0
                    .Else
                        .If Edx == STYPE_PRINTQ
                            AsigText TipoRecurso3, "Impresora."
                            Invoke WriteConsoleA, HandleConsola, Addr TipoRecurso3, 10, 0, 0
                        .Else
                            .If Edx == STYPE_SPECIAL
                                AsigText TipoRecurso4, "Especial."
                                Invoke WriteConsoleA, HandleConsola, Addr TipoRecurso4, 9, 0, 0
                            .Else
    ;                            .If Edx == STYPE_TEMPORARY
    ;                                AsigText TipoRecurso5, " -> Temporal."
    ;                                Invoke WriteConsoleA, HandleConsola, Addr TipoRecurso5, 13, 0, 0
    ;                            .EndIf
                            .EndIf
                        .EndIf
                    .EndIf
   
                .EndIf
            .EndIf
   
            ; Comentario.
            AsigText Comentario, " -> "
              Invoke WriteConsoleA, HandleConsola, Addr Comentario, 4, 0, 0
   
            Mov Edx, [Esi].SHARE_INFO_1.shi1_remark ; Comentario.
   
            Lea Ebx, [Edx]
            Push Ebx
            Invoke UniStrLen, Ebx
            Pop Ebx
            Invoke WriteConsoleW, HandleConsola, Ebx, Eax, 0, 0
   
   
            Invoke ImprimirSaltoDeLinea
   
            ; Avanzar al siguiente elemento en la lista
            add esi, SIZEOF SHARE_INFO_1
            dec ecx
          .Until Ecx == 0
   
   
        invoke GlobalFree, buf
        jmp cleanup



        .EndIf

  .EndIf

handle_error:
    ; Manejo de errores
    cmp eax, ERROR_ACCESS_DENIED
    je access_denied_error
    cmp eax, ERROR_NOT_ENOUGH_MEMORY
    je not_enough_memory_error
    ; Puedes agregar más casos según sea necesario

    ; Otros casos no manejados específicamente
    AsigText Error1, "Error Desconocido."
    Invoke WriteConsoleA, HandleConsola, Addr Error1, SizeOf Error1, 0, 0

    jmp cleanup

access_denied_error:
    ; Código para manejar el error de acceso denegado
    Invoke UniStrLen, Addr fmtAccessDeniedError
    Invoke WriteConsoleW, HandleConsola, Addr fmtAccessDeniedError, Eax, 0, 0
    jmp cleanup

not_enough_memory_error:
    ; Código para manejar el error de falta de memoria
    Invoke UniStrLen, Addr fmtNotEnoughMemoryError
    Invoke WriteConsoleW, HandleConsola, Addr fmtNotEnoughMemoryError, Eax, 0, 0
    jmp cleanup

cleanup:
    Invoke ExitProcess, 0

; Función para obtener una cadena de error a partir de un código de error
GetLastErrorString proc errorCode:DWORD, lpBuffer:PTR WCHAR
    invoke FormatMessageW, FORMAT_MESSAGE_FROM_SYSTEM, 0, errorCode, 0, lpBuffer, 512, 0
    ret
GetLastErrorString endp


; Función para obtener la longitud de una cadena Unicode
UniStrLen PROC _string:DWORD

    mov    eax,_string
    mov    ecx,4
    sub    eax,ecx
@@:
    add    eax,ecx
    mov    edx,DWORD PTR [eax]
    test    dx,dx
    je      @f
    test    edx,0FFFF0000h
    jnz    @b
    add    eax,2
@@: 
    sub    eax,_string
    shr    eax,1
    ret

UniStrLen EndP
ImprimirSaltoDeLinea proc
    ; Imprime un retorno de carro (CR) y una nueva línea (LF) en la consola
    Invoke WriteConsole, HandleConsola, Addr CarroDeRetorno, 1, 0, 0
    Invoke WriteConsole, HandleConsola, Addr NuevaLinea, 1, 0, 0
    ret
ImprimirSaltoDeLinea EndP
PasarADecimal Proc Valor:DWord
        Push Esi
        Push Edi

        Mov Esi, Valor
        Mov Edi, Offset tempbuffer
        Invoke dwtoa, Esi, Edi

        Pop Edi
        Pop Esi

        Ret

PasarADecimal EndP
ArmarArgumentos Proc

LOCAL argc:DWORD

    invoke  GetCommandLineW
    lea    ecx,argc
    invoke  CommandLineToArgvW,eax,ecx

    Mov Esi, Eax
    Add Esi, 4
    mov    ebx,argc
    Xor Edi, Edi

    Push Edi
    Push Esi


    ; Esi apunta al segundo argumento en formato Unicode
    Mov Esi, [Esi]
   
    ; Copiar el segundo argumento a ipbuffer
    Invoke lstrcpynW, Addr serverName, Esi, 256

    Pop Esi
    Pop Edi


;@@:
    Inc Edi
;
;; Convert UNICODE string to ANSI
;
    Invoke WideCharToMultiByte, CP_ACP, 0, \
            DWord Ptr [Esi], -1, Addr buffer, \
            256, 0, 0
;         
    Invoke crt_printf, Addr format, Edi, Addr buffer
;    add    esi,4
;    dec    ebx
;    jnz    @b


    Ret
ArmarArgumentos EndP
End start