I did a modification on the script initially posted by "Slogmeister Extraordinaire" on this question. It is now wrapped in a PowerShell function. As I am no C# programmer I did the adjustments with the returned objects.
Customized script
function Get-SMBSharesViaSMB ($Target) {
# Target may be an IPAddress or a Hostname
# Script initially posted by "Slogmeister Extraordinaire" on this question
$TypeDefinition=@"
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Collections.Generic;
public class GetNetShares {
[DllImport("Netapi32.dll", SetLastError = true)]
static extern int NetApiBufferFree(IntPtr Buffer);
[DllImport("Netapi32.dll", CharSet = CharSet.Unicode)]
private static extern int NetShareEnum(
StringBuilder ServerName,
int level,
ref IntPtr bufPtr,
uint prefmaxlen,
ref int entriesread,
ref int totalentries,
ref int resume_handle);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SHARE_INFO_1 {
public string ShareName;
public uint ShareType;
public string Comment;
public SHARE_INFO_1(string sharename, uint sharetype, string remark) {
this.ShareName = sharename;
this.ShareType = sharetype;
this.Comment = remark;
}
public override string ToString() { return ShareName; }
}
const uint MAX_PREFERRED_LENGTH = 0xFFFFFFFF;
const int NERR_Success = 0;
private enum NetError : uint {
NERR_Success = 0,
NERR_BASE = 2100,
NERR_UnknownDevDir = (NERR_BASE + 16),
NERR_DuplicateShare = (NERR_BASE + 18),
NERR_BufTooSmall = (NERR_BASE + 23),
}
private enum SHARE_TYPE : uint {
STYPE_DISKTREE = 0,
STYPE_PRINTQ = 1,
STYPE_DEVICE = 2,
STYPE_IPC = 3,
STYPE_SPECIAL = 0x80000000,
}
public SHARE_INFO_1[] EnumNetShares(string Server) {
List<SHARE_INFO_1> ShareInfos = new List<SHARE_INFO_1>();
int entriesread = 0;
int totalentries = 0;
int resume_handle = 0;
int nStructSize = Marshal.SizeOf(typeof(SHARE_INFO_1));
IntPtr bufPtr = IntPtr.Zero;
StringBuilder server = new StringBuilder(Server);
int ret = NetShareEnum(server, 1, ref bufPtr, MAX_PREFERRED_LENGTH, ref entriesread, ref totalentries, ref resume_handle);
if (ret == NERR_Success) {
IntPtr currentPtr = bufPtr;
for (int i = 0; i < entriesread; i++) {
SHARE_INFO_1 shi1 = (SHARE_INFO_1)Marshal.PtrToStructure(currentPtr, typeof(SHARE_INFO_1));
ShareInfos.Add(shi1);
currentPtr += nStructSize;
}
NetApiBufferFree(bufPtr);
return ShareInfos.ToArray();
}
else {
ShareInfos.Add(new SHARE_INFO_1(ret.ToString(),10,"ERROR"));
return ShareInfos.ToArray();
}
}
}
"@
function IsValidIPv4Address ($ip) {
return ($ip -match "^\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}$" -and [bool]($ip -as [ipaddress]))
}
if ("GetNetShares" -as [type]) {Write-Verbose "Already registered GetNetShares. Skipping registration."} else {Add-Type -TypeDefinition $TypeDefinition}
$x=[GetNetShares]::new()
$returncodes = $x.EnumNetShares($Target)
Return Errors
$returncodes | where {$."ShareType" -eq "10"} | foreach {
$returnedErrorCode = $."ShareName"
switch ($returnedErrorCode) {
"5" {Write-Error "The user does not have access to the requested information. (Code: $($returnedErrorCode))"}
"124" {Write-Error "The value specified for the level parameter is not valid. (Code: $($returnedErrorCode))"}
"87" {Write-Error "The specified parameter is not valid. (Code: $($returnedErrorCode))"}
"234" {Write-Error "More entries are available. Specify a large enough buffer to receive all entries. (Code: $($returnedErrorCode))"}
"8" {Write-Error "Insufficient memory is available. (Code: $($returnedErrorCode))"}
"2312" {Write-Error "A session does not exist with the computer name. (Code: $($returnedErrorCode))"}
"2351" {Write-Error "The name is not valid. (Code: $($returnedErrorCode))"}
"2221" {Write-Error "Username not found. (Code: $($returnedErrorCode))"}
"53" {
if (IsValidIPv4Address $Target) {
Write-Error "No response from target (Code: $($returnedErrorCode))"
} else {
Write-Error "No response from target. Validate Hostname and DNS resolution. (Code: $($returnedErrorCode))"
}
}
DEFAULT {Write-Error "Unknown Error. (Code: $($returnedErrorCode))"}
}
}
Return objects on success
$returncodes | where {$_."ShareType" -ne "10"}
}
Examples
Current domain
> Get-SMBSharesViaSMB $env:USERDNSDOMAIN
ShareName ShareType Comment
--------- --------- -------
ADMIN$ 2147483648 Remote Admin
C$ 2147483648 Default share
DNS$ 0
IPC$ 2147483651 Remote IPC
NETLOGON 0 Logon server share
print$ 0 Printer Drivers
SYSVOL 0 Logon server share
DNS name "fritz.box" (Example did not answer)
> Get-SMBSharesViaSMB fritz.box
Get-SMBSharesViaSMB : No response from target. Validate Hostname and DNS resolution. (Code: 53)
In Zeile:1 Zeichen:1
+ Get-SMBSharesViaSMB fritz.box
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Get-SMBSharesViaSMB
IP-Address
> Get-SMBSharesViaSMB 192.168.0.1
ShareName ShareType Comment
--------- --------- -------
ADMIN$ 2147483648 Remote Admin
C$ 2147483648 Default share
Hidden$ 0
IPC$ 2147483651 Remote IPC
Localhost (no target defined)
> Get-SMBSharesViaSMB
ShareName ShareType Comment
--------- --------- -------
ADMIN$ 2147483648 Remote Admin
C$ 2147483648 Default share
D$ 2147483648 Default share
IPC$ 2147483651 Remote IPC
Easy to customize
You can make simple adjustments to the results when changing the second last line that is right after the comment Return objects on success.
Here is an example that will include the full path when changing the mentioned line:
$returncodes | where {$_."ShareType" -ne "10"} | Select *, @{Name="FullPath";Expression={"\\" + $Target + "\" + $_.ShareName}}`
The result will look like this:
> Get-SMBSharesViaSMB server.local
ShareName ShareType Comment FullPath
--------- --------- ------- --------
ADMIN$ 2147483648 Remote Admin \\server.local\ADMIN$
C$ 2147483648 Default share \\server.local\C$
IPC$ 2147483651 Remote IPC \\server.local\IPC$
NETLOGON 0 Logon server share \\server.local\NETLOGON
SYSVOL 0 Logon server share \\server.local\SYSVOL