sábado, 11 de julho de 2020

Reverse engineering of a Philips PTA317 SmartTV Skype Camera Part 2

The picture above tells a lot about what I will write in this post. The summary is: It works!
But there's still work to do.

My theory from the previous post seems to be more or less correct. What I did was a application to replay the transfers that I captured from the TV and send the same data from my PC, whatever that data is.

I split the "dump" application in two:
The first reads the dump file and create a c header file containing all the transfers in a array of struct. This file is included in the second application.
The second application do the replay technic, reading the array os transfers in the header file and sending them through USB to the camera.

To do the USB communication part, I first learned of Microsoft's generic driver WinUSB, and started to study it. It is not easy, definitely. I was about to give up when I decided to look for a simpler solution. That's when I found the libusb, and learned that it is used with WinUSB in Windows and is portable to other platforms. This is a big plus, because it should be straight-forward now to port the application for Linux systems and even macOS.
It is also easy to interface. Apart from some x86 vs x64 linking problems, I was able to start to send data to the camera in about 2 hours! The code is so simple that the entire source file has less than 100 lines as I write this text:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// fwsend.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include "libusb.h"

// Firmware dump file
#include "../../firmware_dump.h"

int main()
{
	// Now let's find the USB camera and try to communicate with it
	// Follow the guide in to install the camera as a WinUSB device: https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/winusb-installation
	libusb_context* usbContext;
	if (libusb_init(&usbContext))
	{
		fprintf(stderr, "Failed to init LIBUSB\n");
		return -3;
	}

	// TODO: We only support one camera per system with the function bellow.

	// discover devices
	libusb_device** list;
	libusb_device* found = NULL;
	ssize_t cnt = libusb_get_device_list(usbContext, &list);
	int err = 0;
	if (cnt < 0)
	{
		fprintf(stderr, "Failed to list USB devices\n");
	}
	else
	{

		for (int i = 0; i < cnt; i++) {
			libusb_device* device = list[i];
			struct libusb_device_descriptor descriptor;
			int status = libusb_get_device_descriptor(device, &descriptor);
			if ((status >= 0) && (descriptor.idVendor == 0x0471) && (descriptor.idProduct == 0x2127)) {
				// found
				fprintf(stderr, "Found camera (%d)\n", i);
				libusb_device_handle* devHandle;
				if ((status = libusb_open(device, &devHandle)) != 0)
				{
					fprintf(stderr, "Failed to open camera %d: %d %s\n", i, status, libusb_error_name(status));
				}
				else
				{
					// opened
					// We have a handle. Send the requests....
					int setupPacketsCount = sizeof(firmware) / sizeof(firmware[0]);
					for (int index = 0; index < setupPacketsCount; index++)
					{
						int res = libusb_control_transfer(
							devHandle,
							firmware[index].bmRequestType,
							firmware[index].bRequest,
							firmware[index].wValue,
							firmware[index].wIndex,
							firmware[index].data,
							firmware[index].wLength,
							5 * 1000	// 5 seconds should be enough
						);
						if (res < 0)
						{
							fprintf(stderr, "Control transfer %d failed: %d %s\n", index, res, libusb_error_name(res));
						}
					}
					libusb_close(devHandle);
				}
			}
		}
	}

	libusb_free_device_list(list, 1);
	libusb_exit(usbContext);
}

Sound simple, don't it? The array firmware is declared in the firmware_dunp.h header.

There are still some issues:
  1. WinUSB is a generic USB driver. That means that the device that will use it should, somehow, be installed with the winusb driver. That is no automatic. My next step is to write the inf file to tell Windows to use WinUSB whenever the (firmwareless) camera connects.
    Until then, we have to tell Windows to use WinUSB driver manually. We do it going to the Device Manager, Under Unknown Devices, right click the device corresponding to the camera and opening its Properties window. From there, we go in Install Driver, Search Driver in Computer, Allow me to choose, On the list that appear, we select Universal Serial Bus device and, finally, in the next window, we select WinUSB device, confirming is asked to.
  2. Whenever you change the USB port that the camera is connected to, the step 1 above must be done again, because the driver is installed for that device in that port. In short, it is as if the camera is another device if you change ports. This is important because if the device driver is not WinUSB, the application for transferring the firmware will not work.
  3. Running the fwsend application will transfer the firmware to the camera and it will reset, enumerating again. It will create three devices: Two under "Cameras" and one under Sound Controllers (I think this is the microphone device). The problem is that some Windows applications (Skype including) don't like one of the camera devices. In fact, Skype stays completely frozen until the camera is disconnected. VLC list both cameras, but one of them always give an error when an attempt to open it is made. The solution for this, at least for now, is to go to device manager again and disable the problematic device. I still don't know how to determine which one is the right one to disable. What I do is to disable one (usually the first on the list) and see if the other works (open it in VLC or even Skype configuration). If not, enable it again and disable the other one.

    I don't know why this happen neither how to avoid this problem.
Next step is to write a Windows driver INF file so that Windows installs WinUSB always as the camera driver, and may be even install its firmware automatically. This should solve problems 1 and 2 above.