Friday, July 16, 2010

How to root an HTC Desire using Unrevoked on a 64 bit Windows

Just recently, the "Unrevoked" one click rooting program became available from http://unrevoked.com.

The "Unrevoked" root won't run on a Windows 7 64 bit out of the box, on an HTC desire, because of a missing driver. This can be easily fixed in a few steps.

Some of you might have noticed that neither the HTC sync drivers nor the Android SDK have the "Android Bootloader Interface" drivers for a 64 bit Windows. You can't really get them either.

Luckily, the Android SDK drivers have a 64 bit version of the ABI driver. The problem is, that the USB identification of the device is unknown to the driver. To fix it, we need to add the following line to the "android_winusb.inf" file:

[Google.NTamd64]
%SingleBootLoaderInterface% = USB_Install, USB\VID_0BB4&PID_0C94&REV_0100

This line defines that on a 64 bit machine, if the USB device with a vendor ID of 0x0BB4 (HTC) and a product ID of 0x0C94 (Bootloader interface on HTC Desire), then the driver that should be installed is "%SingleBootLoaderInterface%".

After that, install the driver you altered and that's it.

This is how the USB driver identifies itself (when the driver is not installed, you can look at the hardware IDs of the device identified as "Android 1.0" (which is the bootloader interface before the driver is installed).
Just right click the driver, click Properties, go to Details, and look at the "Hardware Ids" section. You can do the same trick for any other Android device which supports ABI.

Friday, April 23, 2010

Convenient thread logging in Python

I wrote a small logging thread class for easy logging.
My standard error now looks like this:
[Fri Apr 23 15:58:20 2010] [ClipboardReader-1] Starting log...
[Fri Apr 23 15:58:20 2010] [SocksValidator-1] Starting log...
[Fri Apr 23 15:58:20 2010] [SocksChecker-1] Starting log...
Every thread also has its own logfile in the "logs" directory by his class name. I made up the thread name to add the class name of the instance (won't be LoggingThread if you inherit from it).
class LoggingThread(threading.Thread):
def __init__(self, *args, **kwargs):
threading.Thread.__init__(self, *args, **kwargs)
if hasattr(self.__class__, "instance_count"):
self.__class__.instance_count += 1
else:
self.__class__.instance_count = 1

self.name = "%s-%d" % (self.__class__.__name__, self.__class__.instance_count)

if not os.path.isdir("logs"):
os.makedirs("logs")
self.logfile = open("logs/%s.log" % self.name, "w")
self.log("Starting log...")

def log(self, data):
logline = "[%s] [%s] %s" % (time.ctime(), self.name, data)
print >> self.logfile, logline
self.logfile.flush()
print >> sys.stdout, logline

Getting and setting text from the clipboard using Python

Just name this module clipboard.py and use GetClipboardText/SetClipboardText. Very handy, and doesn't really need the win32 extentions, although it's used here.


from ctypes import *
from win32con import CF_TEXT, GHND

OpenClipboard = windll.user32.OpenClipboard
EmptyClipboard = windll.user32.EmptyClipboard
GetClipboardData = windll.user32.GetClipboardData
SetClipboardData = windll.user32.SetClipboardData
CloseClipboard = windll.user32.CloseClipboard
GlobalLock = windll.kernel32.GlobalLock
GlobalAlloc = windll.kernel32.GlobalAlloc
GlobalUnlock = windll.kernel32.GlobalUnlock
memcpy = cdll.msvcrt.memcpy

def GetClipboardText():
text = ""
if OpenClipboard(c_int(0)):
hClipMem = GetClipboardData(c_int(CF_TEXT))
GlobalLock.restype = c_char_p
text = GlobalLock(c_int(hClipMem))
GlobalUnlock(c_int(hClipMem))
CloseClipboard()
return text

def SetClipboardText(text):
buffer = c_buffer(text)
bufferSize = sizeof(buffer)
hGlobalMem = GlobalAlloc(c_int(GHND), c_int(bufferSize))
GlobalLock.restype = c_void_p
lpGlobalMem = GlobalLock(c_int(hGlobalMem))
memcpy(lpGlobalMem, addressof(buffer), c_int(bufferSize))
GlobalUnlock(c_int(hGlobalMem))
if OpenClipboard(0):
EmptyClipboard()
SetClipboardData(c_int(CF_TEXT), c_int(hGlobalMem))
CloseClipboard()

Thursday, April 22, 2010

Here is a really nice piece of code which implements a TCP relay (tunnel) using Python's asyncore module. It's really interesting because the way I implemented it makes a lot of sense, in contrary to the same code written using select.

There's also something about using asyncore.dispatcher_with_send on Unix systems which might send an EWOULDBLOCK sometimes, but I didn't dig deep enough.

The 3 sockets playing a role here are:
* A Relay server socket - accepts connections from clients
* Relay clients, which trigger a new connection to the tunnel destination
* Relay connection for each relay client connected.

The cool thing about asyncore is that you can also decide if handle_read will be called or not, even if data is available, by overriding the "readable" function. In this implementation, a relay client does not start reading from a socket until the relay connection has been established successfully.




class RelayConnection(asyncore.dispatcher):
def __init__(self, client, address):
asyncore.dispatcher.__init__(self)
self.client = client
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
print "connecting to %s..." % str(address)
self.connect(address)

def handle_connect(self):
print "connected."
# Allow reading once the connection
# on the other side is open.
self.client.is_readable = True

def handle_read(self):
self.client.send(self.recv(1024))

class RelayClient(asyncore.dispatcher):
def __init__(self, server, client, address):
asyncore.dispatcher.__init__(self, client)
self.is_readable = False
self.server = server
self.relay = RelayConnection(self, address)

def handle_read(self):
self.relay.send(self.recv(1024))

def handle_close(self):
print "Closing relay..."
# If the client disconnects, close the
# relay connection as well.
self.relay.close()
self.close()

def readable(self):
return self.is_readable

class RelayServer(asyncore.dispatcher):
def __init__(self, bind_address, dest_address):
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.bind(bind_address)
self.dest_address = dest_address
self.listen(10)

def handle_accept(self):
conn, addr = self.accept()
RelayClient(self, conn, self.dest_address)


RelayServer(("0.0.0.0", 8080), ("127.0.0.1", 1234))
asyncore.loop()