OpenWrt: Remote Debugging C/C++ Programs Using GDB

谨以此文献给Hoowa同志,江湖人称“火神”。 —— 题记


Introduction

为调试Linux机器上的C/C++程序,我们通常使用GDB. 但要调试那些在路由器上跑的C/C++程序,GDB还能胜任么?毕竟,我们又不能直接把源码搁到路由器上编译,在Linux机器上编的话还牵扯到交叉编译(Cross Compiling)。

答案是Yes. 在本文中,我就将给大家介绍如何搭建GDB调试环境,如何调试一个比Hello world复杂那么一点点的C程序的方法。

由于我使用的是OpenWrt 14.07 (Barrier Breaker),因此,也假设你也使用的这个版本并对如何编译ipk软件包有一定的了解。Barrier Breaker的官方仓库在这儿:

Compiling GDB Tools

在make menuconfig的时候启用GDB

以及gdbserver

Solution to the “Remote ‘g’ packet reply is too long” problem

在你编译之前呢,我这儿有一个免费的patch送给你,可以避免你以后遇到像下面这样儿式的问题:

700-fix-remote-g-packet-reply-too-long.patch

编译toolchain中的gdb和package中的gdbserver

  • 将700-fix-remote-g-packet-reply-too-long.patch搁到toolchain/gdb/patches/目录下

这时候, 你如果ls一下大约可以看到这些:

  • 编译gdb工具链

  • 编译准备安装到OpenWRT上的gdbserver

将gdbserver扔到路由器上并安装上

路由器上opkg怎么update,怎么装ipk包不用我说了吧?

Add debugging to a package

A more complex Hello world in C

File Listings

  • 文件清单列表如下:

hello/src/hello.c

hello/src/mytest.h

hello/src/mytest.c

hello/src/Makefile

hello/Makefile

Methods for adding debugging to a package

  • 法1: Add CFLAGS to package Makefile and recompile it. # 推荐

正如上面hello/Makefile文件做的那样,为hello软件包的Makefile添加CFLAGS。并重新编译

  • 法2: Recompile the package with CONFIG_DEBUG set # 设置CONFIG_DEBUG=y来编包

  • 法3: Enable debug info in menuconfig #在全局编译构造环境中启用调试开关

这法2和法3是OpenWrt官方wiki上(http://wiki.openwrt.org/doc/devel/gdb )提到的方法,没试验过。

请将编译好的带调试信息的hello软件包扔到路由器上安装。

Starting GDB

Start gdbserver on target (router)

路由器(IP为192.168.1.1)的9000端口上跑gdbserver

Start gdb on host (in compiling tree)

宿主系统(Host System,比如我的是Ubuntu最近的某个版本)上跑gdb

Now gdb shell is up. Set breakpoints, start program, backtrace etc.

这样gdb shell就起来了。设置断点, 启动程序,查看堆栈,就跟以前一样。

Tips

  • The output of hello got printed to the ROUTER console, rather than the Host system terminal.

注意,hello包的输出是打印到路由器的控制台(console/terminal)上的,而非宿主系统上。

  • Always try to use “target remote 192.168.1.1:9000” on Host System

In the gdb shell on the Host system, please always use “target remote 192.168.1.1:9000” to reconnect when the Router process brought up by gdbserver exits and then restarts, as in this way, your previous settings of breakpoints, watchpoints are reserved.

当路由器上gdbserver带起的进程退出后重新带起的时候,请不要直接将宿主系统上的gdb shell退出重进。在原gdb shell中使用”target remote 192.168.1.1:9000″即可,这样做的好处是,你原先关于断点、监测点的设定都会保留住。

How to Decompile OpenWRT Lua Bytecode

最近在玩智能路由器固件的时候,看到某些Lua脚本是乱码。Google了一下,才知道,噢,这是Lua的字节码(bytecode)文件。于是就联想到Python也有字节码文件,还可以反编译(decompile)。不知道Lua是否可以反编译呢?

我找到了一款叫做LuaDec的软件。在Linux系统下,我配置好了luadec,写了一个hello.lua,然后调用luac hello.lua -o hello.luac命令生成了hello.lua的字节码文件hello.luac。我尝试调用luadec hello.luac命令:

 

我很高兴。我想,那能不能把路由器上OpenWRT系统下的Lua字节码文件反编译呢。

第一个想到的就是往路由器上装一个luadec看看。可路由器上空间狭小,不便于施展不说,要交叉编译出在OpenWRT上能跑的luadec,成本上就不划算。又想,Python的字节码就是跨平台的,那Lua的字节码文件是不是也是architecture independent的呢?Google告诉我说是的,你猜的没错。

那接下来的逻辑就是把路由器上的Lua字节码文件搞下来,在Linux上用luadec反编译之。那这样行不行呢?

$ $HOME/bin/luadec ddns.luac
/home/story.me/bin/luadec: ddns.luac: bad header in precompiled chunk

Sorry。那会不会路由器厂家自己做了手脚呢,自己在路由器上写个hello.lua然后luac编译成字节码文件看看?

$ $HOME/bin/luadec hello.luac
/home/story.me/bin/luadec: hello.luac: bad header in precompiled chunk

还是不行。

那还是再问Google. 什么LuaJIT,什么Cocos2d-X,看的头大。最终在OpenWRT官方论坛上看到这样一条线索:

How to Decompile OpenWrt Lua files?
https://forum.openwrt.org/viewtopic.php?id=51469

上面说,根据http://lua-users.org/lists/lua-l/2012-06/msg00065.html的说法:

The OpenWrt Lua is patched and uses a different bytecode format compared to the official vanilla Lua release.

就是说,OpenWRT系统的Lua是打了补丁的,它使用的字节码格式(bytecode format)跟Lua官方的香草美人官方Lua(vanilla Lua)是不一样的。(插一句,Linux官方内核是vanilla kernel,区别于Ubuntu, Redhat等的发行版内核(distribution kernel))。

OpenWRT大神jow说,

Simply compile the official Lua 5.1 sources with the OpenWrt patches apllied on your PC and use the resulting luac binary for decompilation.

一个叫zloop的哥儿们补充道,

The Information about the changed lua bytecode format was already provided - the patches that OpenWrt uses can be found at https://dev.openwrt.org/browser/trunk/package/utils/lua?order=name

很有内涵。抱着试试看的心态,我就试着来搞可以用于反编译OpenWRT上的Lua字节码的luadec工具。嘿,你猜怎么着,成了!

为了防止忘记,我把这一过程的主要步骤记录在这篇博客里。

我是在新近的ubuntu系统上做这事的,它家的包管理工具就是全Debian系都在用的apt-get/dpkg嘛。

luadec的源码找的是这位老兄的Github:https://github.com/viruscamp/luadec

遵照其中编译(Compile)一节的指示,

git clone https://github.com/viruscamp/luadec
cd luadec
git submodule update --init lua-5.1
cd lua-5.1
make linux
cd ../luadec
make LUAVER=5.1

第一步,克隆其git仓库

git clone https://github.com/viruscamp/luadec luadec.git

第二步,进入luadec.git目录,搞到lua-5.1的源码

cd luadec.git
git submodule update --init lua-5.1

第三步,进入lua-5.1目录,编译lua, luac等

cd lua-5.1
make linux

这过程中可能会提示缺少依赖库,比如libncurses,libreadline,可以通过如下命令装上

sudo apt-get install libncurses-dev libreadline-dev

第四步,实际上,上述第三步编译是不必要的,因为我们还没给它打上patch呢。那怎么办?

先make clean清理lua-5.1环境,然后把patch打上嘛。patch从哪儿来,上文中有提到嘛(https://dev.openwrt.org/browser/trunk/package/utils/lua?order=name)

因此,这第四步是先把要给lua-5.1打的补丁们找出来。我有现成的OpenWRT系统开发环境,把它们搞过来就是了:

第五步,开始编译。make linux

可这一步就开始报错。一开始是:

修改src/Makefile文件,

 

然后,make clean; make linux, 编译liblua.so是成功了,可编译lua又报错误:

这个问题的解决跟动态链接库的查找范围和顺序有关,当初啃The Linux Programming Interface的时候很明白,现在却糊涂了。于是也不管它,只管先试试。

最终,解决上述问题后,我对src/Makefile的改动集合如下:

 

加上PKG_VERSION主要是考虑到liblua.so.5.1.5的命名。

第六步,开始编译luadec。

一切顺利。

第七步,准备反编译实验环境。

由于上述步骤生成的lua, luac, luadec都未安装,实验起来嫌麻烦。这时可以把它们安装到系统中去。请注意,如果你的系统中已经安装有lua-5.1, lua-5.2, lua-5.3等环境,自己处理到底是用的哪个lua, 哪个luac, 哪个liblua.so吧。以下,我假设的是一个未安装Linux系统自带的lua-5.1并且之前也未装过lua的Linux环境。

进入到刚才的lua-5.1目录

进到刚才的luadec目录,

 

注意如果/usr/local/lib这条路径不在你的/etc/ld.so.conf文件中,你可能需要sudo ldconfig,否则会can’t find shared library liblua.so.5.1.5诸如此类的错误。

第八步,开始反编译吧。

将之前在OpenWRT系统下生成的Lua字节码文件hello.luac反编译,得到:

成功!

再写个稍微复杂点的,complex.lua

 

在路由器上编译成字节码文件,再到Linux上反编译得到的结果如下:

还不错。Sounds great!

Copyright@来吧,英雄!storypku.com,转载请注明出处。