Hacking on the Asus WL-700gE

My primary reason for owning an Asus WL-700gE is to use the included, built-in bittorrent client to share the large DVD images that contain the latest Fedora Linux releases.  I started out with a 160-Gig model that contains Firmware 1.0.4.6.  Unfortunately, the built-in bittorrent client is based on a faulty code-base that can't handle sharing content larger than 2 Gigabytes.  That's a problem for my purposes, as Fedora Installer DVD images are typically 3 Gigabytes' worth of data.

Other fine hacker efforts, such as those led by KFurge, address this issue by rebuilding the Open-Sourced firmware provided by Asus, and have dropped in command-line versions of bittorrent clients.  If you're a power-user who's comfortable with using Telnet or SSH to access yoru router, you should definitely look into that alternative strategy.

The approach I'm taking is far more time-consuming (and frustrating), because I'm trying to build a drop-in replacement of the ctorrent binary included with Asus' firmware.  Thus, I need to do some reverse-engineering of some Asus-customized protocols used for the Web User Interface to communicate with the bittorrent client; as well performing a clean-room rebuild of ctorrent by using the updated code from the enhanced-ctorrent project.  Enhanced-ctorrent does not exhibit the 2-Gigabyte limitation.

Until I can build such a drop-in replacement, which will be useful in the Web User Interface, I'll need to use the telnet or ssh backdoor into the router and initiate command-line sessions to share the larger Fedora Linux installation DVDs.

[Updated 2007-June-12]
According to a  forum thread at the wl500g.info website, a new Firmware version, 2.0.0.7, is floating in the wild.  Unfortunately, the early reports suggest that large BitTorrent downloads STILL don't work! That's a definite bummer, so it looks like I'll need to continue with my efforts to reverse-engineer the custom ctorrent client in order to replace the binary that is included with the Asus-shipped firmware.

On a positive note, though, the updated Web User Interface bundled with this new Firmware revision has definitely improved.

What I've Discovered About Asus' Ctorrent

To aid in my hacking efforts, I have done the serial-port mod, and am successfully using a Hacked Cell-Phone USB data cable to communicate with the router's serial port (115200,N81). Tip: you might be able to find hackable USB Data Cables, red-tagged (clearance) for $10 or so at your local Radio Shack. Find a Radio Shack store that is "slow" (less turnaround of merchandise -- i.e. in a small neighborhood strip-mall), and hunt around in the cell-phone accessories section.

I have also gone through the exercise of building up a development-environment that allows me to cross-compile enhanced-ctorrent for this router. It was through the exercise of injecting my own ctorrent executable into the firmware that I discovered that my fix was far more involved than simply replacing a binary. Ah well, it's never easy with these things.

Furthermore, I have been spelunking in /proc to examine things like command-line parameters, startup directories, and file-handles. I discovered an undocumented command-line switch is being used, -D. This parameter was added by Asus to identify the Root Downloads Directory -- which I'll call $DLDIR throughout this document. Unfortunately, this conflicts with the -D parameter of enhanced-ctorrent, which uses it to define the Download Rate. No biggie, it's a minor source-code change to enhanced-ctorrent to get things in order.

Anyways, the -D parameter specifies the parent directory of InComplete, Complete, .logs, and .sems -- I'll get to those last two directories in a moment, as they're of particular interest here. Thus, when you run the stock ctorrent client with a proper value for -D, you'll get visual confirmation on the Serial Console that the ctorrent client knows to use the InComplete and Complete directories under the directory passed in via -D.

From my observations, the stock ctorrent client has been modified to write a "status update" file into the $DLDIR/.logs directory (using the process id as part of the filename). This file is repeatedly read by the stock giFT daemon, which in turn maintains statistics that are displayed in the Web GUI. (Torrent Filename, Size, Protocol, etc..). It's pretty easy to snarf a copy of a log file of a running ctorrent process, strategically twiddle a few bytes, and see how they impact what's displayed in the WebGUI. The only tricky part is that you'd need to hijack a freshly-launched ctorrent process to do so.

At this point, all I'm really interested in discovering here are the simple metrics, like percentage complete and process status (Processing, Paused, Stopped, etc.). At least figuring out how to decode the Torrent Filename is pretty obvious, as it's plain-text. :o)

The $DLDIR/.sems directory contains a "sem" file (again, uniquely identified by process-id), which I believe is nothing more than a Process-Control Pipe that allows the giFT Daemon to send runtime-commands to the ctorrent client.

Recent Developments

2007-May-20 - Successful Debug-Tracing!

I managed to trace-log a session under the router, and now have enough to start building in "Asus-Compatible" extensions to Enhanced-CTorrent. There's now a light at the end of the tunnel, and I should have a first cut working within the week. Depending on how ambitious I get with the Hex-Calculator, getting all of the "Status Counters" working under Download Manager may take a bit longer.

2007-May-13 - Fixed Sources to compile on Kernels > 2.6.18

I rebuilt my development environment over the weekend, getting rid of the Debian under VMWare solution, in favor of using the environment of my native Fedora workarea. I was really only using Debian to work around some issues in other experimental attempts, and Debian uses a kernel version that doesn't suffer from the UTS_RELEASE undefined problem.

It seems that recent Linux Kernels have pulled the carpet on nfs_utils -- instead of including linux/version.h, you need to include linux/utsrelease.

Thus, the contents of my /opt/WL700g/nasoc/src/apps/nfs-utils/tools/getkversion/getkversion.c would ideally read as follows:

/*
 * Get version number of the kernel this was compiled for.
 * This is NOT the same as calling uname(), because we may be
 * running on a different kernel.
 */

#include "config.h"

#include <linux/utsrelease.h>
#include <stdio.h>

int
main(void)      /* This is for Dan Popp ;) */
{
        printf("%s\n", UTS_RELEASE);
        return 0;
}

Unfortunately for me, that doesn't quite work in my build-environment. For some daffy reason that I've got to track down, including <linux/utsrelease.h> fails on Fedora 7. I'll figure this out later, I'm too lazy to fix this seemingly useless nfs-utils tool right now...

I also needed to add the -lcrypt flag to pppd/pppd/Makefile. Interesting that it compiled fine under Debian, but not in Fedora 7. *Shrug*. Ah well. Moving on . . .

2007-May-11 - Multiple versions of ctorrent exist in the Build Environment.

ctorrent-1.3.4.5.e reveals the useful information:

# ./ctorrent -e0 -D/shares/MYVOLUME1/MYSHARE1/Download


 ctorrent-1.3.4.5.e

complete path is /shares/MYVOLUME1/MYSHARE1/Download/Complete
incomplete path is /shares/MYVOLUME1/MYSHARE1/Download/InComplete
CTorrent devel          Copyright: YuHong(992126018601033)
WARNING: THERE IS NO WARRANTY FOR CTorrent. USE AT YOUR OWN RISK!!!

Generic Options:
-h/-H           Show this message.
-x              Decode metainfo(torrent) file only, don't download.
-c              Check exist only. don't download.

Download Options:
-e int          Exit while seed <int> hours later. (default 72 hours)
-p port         Listen port. (default 2156 -> 2106)
-s save_as      Save file/directory/metainfo as... 
-C cache_size   Cache size,unit MB. (default 16MB)
-f              Force seed mode. skip hash check at startup.
-b bf_filename  Bit field filename. (use it carefully)
-M max_peers    Max peers count.
-m min_peers    Min peers count.
-B rate         Max bandwidth (unit KB/s)
-P peer_id      Set Peer ID [-CT1304-]

Make metainfo(torrent) file Options:
-t              With make torrent. must specify this option.
-u url          Tracker's url.
-l piece_len    Piece length.(default 262144)

eg.
hong> ctorrent -s new_filename -e 12 -C 32 -p 6881 eg.torrent

home page: http://ctorrent.sourceforge.net/
bug report: bsdi@sina.com

 

ctorrent-1.3.4.700gE.9.a - does not give much useful information -- the InComplete/Complete? directory knowledge doesn't show up:

# ./ctorrent -e0 -D/shares/MYVOLUME1/MYSHARE1/Download


 ctorrent-1.3.4.700gE.9.a

CTorrent devel          Copyright: YuHong(992126018601033)
WARNING: THERE IS NO WARRANTY FOR CTorrent. USE AT YOUR OWN RISK!!!

Generic Options:
-h/-H           Show this message.
-x              Decode metainfo(torrent) file only, don't download.
-c              Check exist only. don't download.

Download Options:
-e int          Exit while seed <int> hours later. (default 72 hours)
-p port         Listen port. (default 2156 -> 2106)
-s save_as      Save file/directory/metainfo as... 
-C cache_size   Cache size,unit MB. (default 16MB)
-f              Force seed mode. skip hash check at startup.
-b bf_filename  Bit field filename. (use it carefully)
-M max_peers    Max peers count.
-m min_peers    Min peers count.
-B rate         Max bandwidth (unit KB/s)
-P peer_id      Set Peer ID [-CT1304-]

Make metainfo(torrent) file Options:
-t              With make torrent. must specify this option.
-u url          Tracker's url.
-l piece_len    Piece length.(default 262144)

eg.
hong> ctorrent -s new_filename -e 12 -C 32 -p 6881 eg.torrent

home page: http://ctorrent.sourceforge.net/
bug report: bsdi@sina.com

2007-May-11 - Special Files ?

 

  • $DLDIR/.logs/dm.jqs
  • $DLDIR/.sems/sem.jqs

It seems that ctorrent was coded to recognize those two as well. When I launch ctorrent solely on the command-line (without the help of giFT or snarf), giFT still knows about it. Despite my renaming the files (and creating zero-length substitutes as plain-files), the Asus-customized ctorrent client knows how to send a signal to giFT -- it's not happening through these files:

# /apps/bin/ctorrent-oob -e0 -D/shares/MYVOLUME1/MYSHARE1/Download f7-test4-live-i386.torrent


 ctorrent-1.3.4.700gE.9.a

sem error :: No such file or directory
Cannot open semsem error :: No such file or directory
Cannot open semsem error :: No such file or directory
Cannot open semf7-test4-live-i386F-6.93-i386-Live.isoSHA1SUMf7-test4-live-i386F-6.93-i386-Live.isoSHA1SUMf7-test4-live-i386F-6.93-i386-Live.isoSHA1SUMf7-test4-live-i386F-6.93-i386-Live.isoSHA1SUM

guest_from (suit_num) is 1 (0:BIG5 ,1:GB2312, -1:UTF-8), possible sets are 2
gift: err completed : send fail
gift: err completed : send fail
gift: err completed : send fail
sem error :: No such file or directory
sem error :: No such file or directory
sem error :: No such file or directory
sem error :: No such file or directory
sem error :: No such file or directory
sem error :: No such file or directory
sem error :: No such file or directory
ctorrent term write
# 
# 
# ls -l ../.sems
-rw-r--r--    1 root     root            0 May 11 21:35 sem.jqs
prw-r--r--    1 root     root            0 May 11 21:33 sem.jqs.oob
# sem error :: No such file or directory
ls -l ../.logs
--wT--T--t    1 root     root            0 May 11 21:33 dm.jqs.oob
----------    1 root     root          768 May 11 21:37 log.531
# sem error :: No such file or directory
sem error :: No such file or directory
sem error :: No such file or directory
sem error :: No such file or directory
sem error :: No such file or directory
sem error :: No such file or directory
sem error :: No such file or directory

Loud Thinking

Hmm, the parent (caller) probably creates the .sem file, and the child (ctorrent) continually tries to open the .sem file. The parent gets back the Process-ID of the child from the exec() or system() call, and uses it to construct the .sem filename.

The child creates the .log file, free-and-clear. It also communicates this fact to giFT, as gift comes back with "gift: err completed : send fail". I'm guessing ctorrent is auto-failing because it can't open its sem file?

My Next Steps

I need to make a "wrapper" application that emulates the Logs and Sems behavior. The wrapper will then launch the real ctorrent client and proxy all traffic that run across the pipe -- while simultaneously dumping traffic out to the Serial Console. This is pretty much identical in concept to tee, and I'm only doing it to aid my understanding of what WebGUI Actions correspond to what process-control commands over the pipe.

Once I have the full list of commands that I'd need to reimplement, I can begin writing a patch to enhanced-ctorrent to emulate all of the Customized Behavior that Asus put into the original ctorrent: Using $DLDIR/InComplete as a staging area, moving completed files to $DLDIR/Complete, logging progress in $DLDIR/.logs, and accepting process-control commands via a sem file in $DLDIR/.sems.

If things go well, I should have this wrapped up in another weekend or two. If you're tracking this project and I've been dragging ass on this, though, please ping me through the Comments Section below. I often bounce around between several projects, which means that some things can wind up on hold for quite some time. I can't tell that anyone (aside from me) is interested in seeing this through in a timely fashion without some feedback. :-)

Right now, my goal (which is aggressive) is to have this ready by the time Fedora 7 is released. I bought this router for the sole purpose of downloading and sharing those huge Installer-DVD iso files, and they're way larger than the 2-Gigabyte limitation I'm encountering with this router. Thus, without this fix, the router is absolutely useless for the purpose I originally bought it for.

Still, though, it's a nice, hackable device that I'm going to tear into a lot more -- I just wanted the stock firmware to tide me over for a few months. *Grumble* *Grumble*

Older Notes on Recompiling the Firmware

(Yea, I know this procedural description sucks.  It'd be nice to rework this into a bash-script that automagically downloads the file and does all of this, but that's a low priority item on my huge task list.  Sorry.)

  • Obtain the Sources
    • Go to the WL-700gE Support Site
    • Enter "wl-700ge" in the search-box
    • Scroll down to the latest GPL source code for firmware version 1.0.4.6 at this writing)
    • Download from the appropriate mirror for your region
       
  • Unpack the sources to /opt
    • cd /opt - yes, it must be in /opt.
    • unzip GPL_WL700gE_1046.zip - this produces a LICENSE text file, and two tarballs: One for the toolchain, and one for the sources.
    • tar -xvzpf toolchain_wl700ge.tar.gz - this unpacks the toolchain to ./brcm
    • tar -xvzpf GPL_WL700gE_1.0.4.6.tar.gz - this unpacks the sources to ./WL700g
       
  • Working Environment Adjustments
    • ln -s /bin/bash /bin/ash - the build-scripts really want the ash shell, which is a scaled-down version of bash. If you want, you can install a "real" ash like this one for FC3.
    • ln -s /sbin/mkfs.cramfs /sbin/mkcramfs - the build scripts expect and use mkcramfs, so we'll just create a symbolic link to finesse around this.
    • chown -R $USER:$USER /opt/brcm /opt/WL700g - this makes sure you "own" all the files. (The second $USER in "$USER:$USER" is really to name the Group ID. I'm assuming that your Group ID is the same as your User ID - which is the default in Fedora).
    • chmod u+w -R WL700g/* - this makes sure you can write all the files.
    • find WL700g -type d -exec chmod u+x {} \; - this makes sure you can navigate through all the direcctories.
    • export PATH=$PATH:/opt/brcm/hndtools-mipsel-uclibc/bin:/opt/brcm/hndtools-mipsel-linux/bin
    • cd /opt/WL700g/nasoc/src/apps
       
  • Customize / Adjust Busybox Settings
    • make menuconfig
    • mv Config_ASUS Config_ASUS.oob ; mv .config Config_ASUS - save the original (out-of-box) Asus Apps configuration, and move our new version in its place.
       
  • Build / Rebuild Firmware Apps
    • make clean && make rebuild

Additional References

This article couldn't have been put together without referring to older documentation: