Access HKCU from TAPI Service Provider - tapi

I am writing an adapter TSP for a phone system. This system has a TAPI API but it is incompatible with the application I am trying to TAPI-enable. In order to place a call from the correct line, I need to know some information (from HKCU) about who is making the request. Since the TSP runs in the context of the Telephony service, I cannot access is directly. My plan was to use the functionality of LINE_CREATEDIALOGINSTANCE to read this information.
The problem I'm having is that the Telephony service is crashing immediately after returning from TUISPI_providerGenericDialog with the following stack trace:
72004400()
tapisrv.dll!_FreeDialogInstance#20() + 0xa93 bytes
tapisrv.dll!_ClientRequest#16() + 0x8f bytes
rpcrt4.dll!_Invoke#12() + 0x30 bytes
rpcrt4.dll!_NdrStubCall2#16() + 0x217 bytes
rpcrt4.dll!_NdrServerCall2#4() + 0x19 bytes
rpcrt4.dll!_DispatchToStubInCNoAvrf#12() + 0x17 bytes
rpcrt4.dll!RPC_INTERFACE::DispatchToStubWorker() + 0xae bytes
rpcrt4.dll!RPC_INTERFACE::DispatchToStub() + 0x4b bytes
rpcrt4.dll!LRPC_SCALL::DealWithRequestMessage() + 0x1d5 bytes
rpcrt4.dll!LRPC_ADDRESS::DealWithLRPCRequest() + 0x90 bytes
rpcrt4.dll!LRPC_ADDRESS::ReceiveLotsaCalls() + 0x20c bytes
rpcrt4.dll!RecvLotsaCallsWrapper() + 0xd bytes
rpcrt4.dll!BaseCachedThreadRoutine() + 0x92 bytes
rpcrt4.dll!ThreadStartRoutine() + 0x1b bytes
kernel32.dll!_BaseThreadStart#8() + 0x34 bytes
As per this book, the Telephony service will crash if TSPI_providerFreeDialogInstance is not implemented. I have implemented this function and DepWalker shows it as being properly exported. ApiSpy32 shows that its address is correctly returned via GetProcAddress when my TSP is loaded. Why is it still crashing?
The relevant code:
LONG TSPIAPI TSPI_lineMakeCall(DRV_REQUESTID dwRequestID, HDRVLINE hdLine, HTAPICALL htCall,
LPHDRVCALL lphdCall, LPCWSTR lpszDestAddress, DWORD dwCountryCode, LPLINECALLPARAMS const lpCallParams)
{
OutputDebugString("TSPI_lineMakeCall\n");
PDRVLINE pLine = (PDRVLINE) hdLine;
*lphdCall = (HDRVCALL)hdLine;
typedef TUISPICREATEDIALOGINSTANCEPARAMS PARAMS;
pLine->htCall = htCall;
DWORD lLength = (lstrlenW(lpszDestAddress) + 1) * sizeof(WCHAR);
PARAMS* lParams = (PARAMS*)DrvAlloc(sizeof(PARAMS) + lLength);
RtlZeroMemory(lParams, sizeof(PARAMS) + lLength);
lParams->dwRequestID = dwRequestID;
lParams->hdDlgInst = (HDRVDIALOGINSTANCE)1000;
lParams->lpszUIDLLName = L"TapiAdapter.tsp";
lParams->lpParams = lParams + 1;
lParams->dwSize = lLength;
lstrcpyW((LPWSTR)(lParams + 1), lpszDestAddress);
(*pLine->pfnEventProc)(pLine->htLine, 0, LINE_CREATEDIALOGINSTANCE, (DWORD)lParams, 0, 0);
return dwRequestID;
}
LONG TSPIAPI TSPI_providerGenericDialogData(DWORD_PTR dwObjectID, DWORD dwObjectType, LPVOID lpParams, DWORD dwSize)
{
OutputDebugString("TSPI_providerGenericDialogData\n");
return 0;
}
LONG TSPIAPI TSPI_providerFreeDialogInstance(HDRVDIALOGINSTANCE hdDlgInst)
{
OutputDebugString("TSPI_providerFreeDialogInstance\n");
return 0;
}
LONG TSPIAPI TUISPI_providerGenericDialog(TUISPIDLLCALLBACK lpfnUIDLLCallback, HTAPIDIALOGINSTANCE htDlgInst, LPVOID lpParams, DWORD dwSize, HANDLE hEvent)
{
SetEvent(hEvent);
LPCWSTR lNumber = (LPCWSTR)lpParams;
MessageBoxW(0, lNumber, L"Dial Number", MB_OK);
return 0;
}

I don't know but the help for the TUISPICREATEDIALOGINSTANCEPARAMS Structure says that lpszUIDLLName should be a ...
pointer to a NULL-terminated string
specifying the fully qualified name of
the UI DLL to load in the application
context
... however L"TapiAdapter.tsp" doesn't look like a fully qualified name of the UI DLL ("fully-qualified" means that it includes the path name). Do you have a UI DLL to be loaded? Is it loaded? Does it display the dialog? Is it unloaded? Does TUISPI_providerGenericDialog exist in your TSP, or does it existin your UI DLL (they're supposed to be two different DLLs)?

I have found the solution: As per MSDN, the first parameter of the LINEEVENT call for this event only needs to be an HPROVIDER, not an HTAPILINE. Since the first parameter of LINEEVENT is of type HTAPILINE, the HPROVIDER will need to be cast.

Related

Why is CDOSYS sending me a type 3 message with a 56 byte response?

I have some really old code that I'd like to get working with the CDO.Message object. Many years ago, I think this code did work (Windows 98?), but then it didn't work starting with Windows XP (now I'm on Windows 10).
My code first checks sResponse2.cLength, and if it's 24, then it performs the NTLM calculation. Failing that, if sResponse1.cLength is also 24, then it performs the LM calculation instead. That was all I ever had implemented.
Today, with the CDO.Message testing, it took the LM code path, and the hash that my code calculated did not match what CDO.Message provided. That's another problem.
What I'm more interested in right now is why sResponse2.cLength is 56. What does a value of 56 indicate? Given that value and the flags, should I be testing the type 3 message response using a different algorithm?
When I send the type 2 message to the client, I'm only specifying these two flags:
#define F_NEGOTIATE_OEM 0x00000002
#define F_NEGOTIATE_NTLM 0x00000200
The response was greater than 24 bytes because it was an NTLMv2 response. The 56 bytes include the 16-byte HMAC-MD5 hash, a blob signature, two "reserved" fields, an 8-byte time stamp, an 8-byte client nonce, and variable length target data (12 bytes of overhead in this case). 56 bytes may be the minimum possible size with an empty target server/domain string.
Had it been 24 bytes, it could have instead been an NTLMv2 Session Response, which is different.
Details can be found here:
http://davenport.sourceforge.net/ntlm.html
I've created the following code to parse the NTLMv2 response:
struct TYPE3_BLOCK
{
const BYTE* pcbHash; // 16 bytes
const BYTE* pcbBlockPtr; // Points to the block after the hash
DWORD cbBlockPtr;
DWORD dwSignature;
FILETIME ftStamp;
const BYTE* pcbNonce; // 8 bytes
DWORD dwReserved1;
DWORD dwReserved2;
const BYTE* pcbTarget; // Remainder, less dwReserved2
DWORD cbTarget;
};
template <typename T>
BOOL TConsumePtr (const BYTE*& pcbData, DWORD& cbData, T** pptPtr, DWORD cbPtr)
{
if(cbData >= cbPtr)
{
*pptPtr = reinterpret_cast<T*>(pcbData);
pcbData += cbPtr;
cbData -= cbPtr;
return TRUE;
}
return FALSE;
}
template <typename T>
BOOL TConsumeData (const BYTE*& pcbData, DWORD& cbData, T* ptPtr)
{
if(cbData >= sizeof(T))
{
CopyMemory(ptPtr, pcbData, sizeof(T));
pcbData += sizeof(T);
cbData -= sizeof(T);
return TRUE;
}
return FALSE;
}
BOOL CrackType3Response (const BYTE* pcbResponse, DWORD cbResponse, __out TYPE3_BLOCK* pBlock)
{
if(TConsumePtr(pcbResponse, cbResponse, &pBlock->pcbHash, 16))
{
pBlock->pcbBlockPtr = pcbResponse;
pBlock->cbBlockPtr = cbResponse;
if(TConsumeData(pcbResponse, cbResponse, &pBlock->dwSignature) &&
TConsumeData(pcbResponse, cbResponse, &pBlock->dwReserved1) &&
TConsumeData(pcbResponse, cbResponse, &pBlock->ftStamp) &&
TConsumePtr(pcbResponse, cbResponse, &pBlock->pcbNonce, 8))
{
pBlock->cbTarget = cbResponse - sizeof(pBlock->dwReserved2);
return TConsumePtr(pcbResponse, cbResponse, &pBlock->pcbTarget, pBlock->cbTarget) &&
TConsumeData(pcbResponse, cbResponse, &pBlock->dwReserved2);
}
}
return FALSE;
}

GetRegionData() not working for stack allocated buffer

I am using the win32 function GetRegionData(...) to extract the exact rectangles which make up the invalidated paint region in response to a WM_PAINT message.
The following code works correctly and the second call to GetRegionData succeeds.
DWORD uRegionSize = GetRegionData(hRgn, sizeof(RGNDATA), NULL); // Send NULL request to get the storage size
RGNDATA* pData = (RGNDATA*)(new char[uRegionSize]); // Allocate space for the region data
pData->rdh.dwSize = uRegionSize;
DWORD uSizeCheck = GetRegionData(hRgn, uRegionSize, pData);
if (uSizeCheck != uRegionSize) {
// FAIL!
delete[] pData;
return;
}
...
do stuff with rectangles
...
But when I tried to move the data buffer to a member variable allocated on the stack, GetRegionData fails every time returning 0.
In my header:
char UpdateRegionData[LOTS_MORE_BYTES_THAN_NEEDED];
In my cpp:
DWORD uRegionSize = GetRegionData(hRgn, sizeof(RGNDATA), NULL); // Send NULL request to get the storage size
RGNDATA* pData2 = (RGNDATA*)UpdateRegionData;
pData2->rdh.dwSize = uRegionSize;
DWORD uSizeCheck = GetRegionData(hRgn, uRegionSize, pData2);
if (uSizeCheck != uRegionSize) {
// FAIL!
return;
}
The only thing different between the 2 versions is the memory allocation, but the second one fails. GetLastError() returns code 183 which is ERROR_ALREADY_EXISTS which doesn't seem to make much sense.
Thanks to Raymond for pointing out the size error - that was indeed an error, but it was not cause of the issue. The actual cause was byte alignment. The project I am working on has its byte alignment set to a default of 1. When I specified 4 byte alignment for the buffer using __declspec(align(4)) then the problem was solved.

How to get a serial number of a Windows disk?

I'm trying to get a serial number of a disk, using IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER:
HANDLE h = CreateFile ("\\\\.\\PhysicalDrive0", GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING,
FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, 0);
if (h != INVALID_HANDLE_VALUE) {
struct {
USHORT Reserved;
USHORT SerialNumberLength;
UCHAR SerialNumber[252];
} dsn;
DWORD nr;
memset(&dsn, '\0', sizeof dsn);
if ((DeviceIoControl(h, IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER,
NULL, 0, &dsn, sizeof(dsn), &nr, 0))) {
printf("Serial number: %s\n", dsn.SerialNumber);
} else {
printf("No serial number, error %d.\n", (int)GetLastError());
}
}
However, GetLastError() returns ERROR_INVALID_FUNCTION.
The disk does exist, and it has a serial number, see this registry entry:
How can I retrieve the serial number from C code without using the registry?
we can use IOCTL_STORAGE_QUERY_PROPERTY with StorageDeviceProperty (Indicates that the caller is querying for the device descriptor, STORAGE_DEVICE_DESCRIPTOR)
and use SerialNumberOffset member of STORAGE_DEVICE_DESCRIPTOR
Specifies the byte offset from the beginning of the structure to a
NULL-terminated ASCII string that contains the device's serial number.
If the device has no serial number, this member is zero.
code can look like this:
ULONG GetSerial(HANDLE hFile)
{
static STORAGE_PROPERTY_QUERY spq = { StorageDeviceProperty, PropertyStandardQuery };
union {
PVOID buf;
PSTR psz;
PSTORAGE_DEVICE_DESCRIPTOR psdd;
};
ULONG size = sizeof(STORAGE_DEVICE_DESCRIPTOR) + 0x100;
ULONG dwError;
do
{
dwError = ERROR_NO_SYSTEM_RESOURCES;
if (buf = LocalAlloc(0, size))
{
ULONG BytesReturned;
if (DeviceIoControl(hFile, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), buf, size, &BytesReturned, 0))
{
if (psdd->Version >= sizeof(STORAGE_DEVICE_DESCRIPTOR))
{
if (psdd->Size > size)
{
size = psdd->Size;
dwError = ERROR_MORE_DATA;
}
else
{
if (psdd->SerialNumberOffset)
{
DbgPrint("SerialNumber = %s\n", psz + psdd->SerialNumberOffset);
dwError = NOERROR;
}
else
{
dwError = ERROR_NO_DATA;
}
}
}
else
{
dwError = ERROR_GEN_FAILURE;
}
}
else
{
dwError = GetLastError();
}
LocalFree(buf);
}
} while (dwError == ERROR_MORE_DATA);
return dwError;
}
also for open device we can use CreateFileW (L"\\\\.\\PhysicalDrive0", 0, 0, 0, OPEN_EXISTING, 0, 0); - in place dwDesiredAccess we can use 0 because IOCTL_STORAGE_QUERY_PROPERTY defined as
CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) - so FILE_ANY_ACCESS - accept any file access and FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING have sense only for file system devices (more general which use cache) - for disk devices - this is irrelevant
I have tried different approaches and figured out that sending IOCTL_STORAGE_QUERY_PROPERTY doesn't work as expected for different USB devices in both User and Kernel mode code. For some USB mass storages it doesn't return serial number. I'd assume that there are 2 correct ways to do that:
IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER (a few AV products do that?)
create and send URB_CONTROL_DESCRIPTOR_REQUEST
Update 1.
I saw using IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER in the one file system mini-filter driver that was used like the following:
FltGetDiskDeviceObject( FltObjects->Volume, &pDevice );
Irp = IoBuildDeviceIoControlRequest(
IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER,
Device,
...
Irp = IoBuildDeviceIoControlRequest(
IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER,
Device,....
I haven't tested this yet in KernelMode code, unfortunately, but trying to make it works in user mode code shows that this IOCTL mostly
is not supported by different devices, maybe this IOCTL is reserved for the future as a standard way to get the serial number and will be
required by USB standards later?
Also, "wmic diskdrive get name, serialnumber" returns in my case for USB Mass Storage incorrect serial number = "E" the same result as we would use IOCTL_STORAGE_QUERY_PROPERTY.
So, the correct way to get the serial number of USB mass storage is creating a USB request block in KernelMode code and using DeviceIoControl to the hub driver in the UserMode code.
USBVIEW (UserMode code) gets serial number by sending IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX ioctl to the HUB driver which
returns USB_NODE_CONNECTION_INFORMATION_EX that contains USB_DEVICE_DESCRIPTOR. iSerialNumber member of USB_DEVICE_DESCRIPTOR is used later in the
IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION ioctl request to the hub driver which finally gets serial number.
Another approach I see is maybe using some 3-rd party libraries like libusb to simplicate all these actions...
Update 2.
I took a look at USBSTOR disassembled code. USBSTOR_DeviceControl routine has the following code for the IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER
++v3->CurrentLocation;
++v3->Tail.Overlay.CurrentStackLocation;
v8 = IofCallDriver(*(PDEVICE_OBJECT *)(v6 + 24), v3);
So, it passes the IRP down the stack to the usbhub driver as was expected. So maybe this functionlaty is expected to be realized in
the usbhub driver sometime ? That would be great as for me...

C Program Strange Characters retrieved due to language setting on Windows

If the below code is compiled with UNICODE as compiler option, the GetComputerNameEx API returns junk characters.
Whereas if compiled without UNICODE option, the API returns truncated value of the hostname.
This issue is mostly seen with Asia-Pacific languages like Chinese, Japanese, Korean to name a few (i.e., non-English).
Can anyone throw some light on how this issue can be resolved.
# define INFO_SIZE 30
int main()
{
int ret;
TCHAR infoBuf[INFO_SIZE+1];
DWORD bufSize = (INFO_SIZE+1);
char *buf;
buf = (char *) malloc(INFO_SIZE+1);
if (!GetComputerNameEx((COMPUTER_NAME_FORMAT)1,
(LPTSTR)infoBuf, &bufSize))
{
printf("GetComputerNameEx failed (%d)\n", GetLastError());
return -1;
}
ret = wcstombs(buf, infoBuf, (INFO_SIZE+1));
buf[INFO_SIZE] = '\0';
return 0;
}
In the languages you mentioned, most characters are represented by more than one byte. This is because these languages have alphabets of much more than 256 characters. So you may need more than 30 bytes to encode 30 characters.
The usual pattern for calling a function like wcstombs goes like this: first get the amount of bytes required, then allocate a buffer, then convert the string.
(edit: that actually relies on a POSIX extension, which also got implemented on Windows)
size_t size = wcstombs(NULL, infoBuf, 0);
if (size == (size_t) -1) {
// some character can't be converted
}
char *buf = new char[size + 1];
size = wcstombs(buf, infoBuf, size + 1);

Use ReadProcessMemory to record pointed instructions

I'm trying to log pointed instructions with ReadProcessMemory, in fact I use EIP register to get the next insctruction address. Next, I use distorm lib to display mnemonic. But ReadProcessMemory reads nothing.
void display_instruction(Debuggee* debuggee)
{
CONTEXT lcContext;
lcContext.ContextFlags = CONTEXT_ALL;
GetThreadContext(debuggee->debugEvent->u.CreateProcessInfo.hThread, &lcContext);
BYTE cInstruction = 0;
DWORD dwReadBytes;
ReadProcessMemory(debuggee->debugEvent->u.CreateProcessInfo.hProcess, (void*)&lcContext.Eip, &cInstruction, 1, &dwReadBytes);
decode((void*)cInstruction); //Distorm Mnemonic
printf("Instruction : 0x%03.3X , %d\n",cInstruction,dwReadBytes);
}
}
I need your help please !^^
This probably:
ReadProcessMemory(debuggee->debugEvent->u.CreateProcessInfo.hProcess,
(void*) &lcContext.Eip, // <
&cInstruction,
1,
&dwReadBytes);
should be:
ReadProcessMemory(debuggee->debugEvent->u.CreateProcessInfo.hProcess,
(void*) lcContext.Eip, // <
&cInstruction,
1,
&dwReadBytes);
as ReadProcessMemory expects the address in the virtual memory of the target process.
plus you can check the return value and the reason of failure.

Resources