Reversing QT applications - Part I


Damn.. this one remained in oblivion for years!.. This essay is about QT (@ trolltech) runtime debugging. It's the first of 2 articles about qt runtime manipulation. In this one we get a tree of objects from a qt applicatión, locating their main structure address memory pool. Once located a widget enable or disable buttons or menu items is a simple task.

This little tool called qtree when attached to a qt application (tested in qt2.x and qt3.x) will dump it's objets tree. Now it may be outdated, as qt is now free.

Introduction

I'll asume the basics about qwidgets (qt programming). C++ and gdb skills are also a requisite, as some steps relay in them.

In order to generate a tree with the controls of an application there are several steps to walk..

QT reversing stuff could be Download from here: qtrev-doc-src.tar.gz


Parent and childs

Let's focus at qwidget class for this topic (see qt documentation for help). Any widget can have one (and only one) parent, while at the same time can hold several childs. That's the nature of a qt application GUI. A fragment of qobject-h.html, inherited by qwidget class, holds parent and childs members declarations of a widget (private section of class) :
...
static QMetaObject *metaObj;
const char *objname;
QObject *parentObj;
QObjectList *childObjects;
QSignalDict *connections;
...
QObject class export methods for these members (qobject.html):
QObject * QObject::parent () const
Returns a pointer to the parent object.
See also: children().
const QObjectList * QObject::children () const
Returns a list of child objects, or 0 if this object 
has no children.
The QObjectList class is defined in the qobjcoll.h header file. The latest child added is the first object in the list and the first child is added is the last object in the list.

Note that the list order might change when widget children are raised or lowered. A widget that is raised becomes the last object in the list. A widget that is lowered becomes the first object in the list. See also: queryList(), parent(), insertChild() and removeChild().

We can get from the widget his parent directly reading the pointer *parentObj, as it only can have one. But for childs, we'll need to go trough a complete list of them reading each entry of childObjects.

So, in order to continue, let's see how the QObjectList class work once compiled.

Qt list interface

The QObjectList class (see qobjectlist.h for more info) is an overloaded QList class of QObjects. Also, QList inherits QGList as main collections interface. This is the most important fragment (private declaration of qglistclass at qglist-h.html):
...
QLNode *firstNode; // first node
QLNode *lastNode; // last node
QLNode *curNode; // current node
int curIndex; // current index
uint numNodes; // number of nodes
QGList *iterators; // list of iterators
...

What we found here is a fast access to list items, and also a new list to iterators. The fast access interface (firstNode, curNode, lastNode pointers and curIndex) will allow as to read the whole list of items. We can't relay in iterators member as it may be uninitialized. Each member of a list is a QLNode class.


List nodes in qglist class

The extract form qglist-h.html:
...
QCollection::Item data
QLNode *prev;
QLNode *next;
...
QCollection::Item data is the answer we are looking for: it's the pointer to the object in the memory (a pointer to a widget lstructure layout as seen in basics). That's, it's the stored item in the list. Let's go further..


Debugging aproach..

Ok, let's start with a very simple example. Time to go into qt2-doc directory and enter t4 example's (usally /usr/share/at2-doc/tutorial/t4/ ) one. If we edit the main.cpp file, we can see something like:
...
class MyWidget : public QWidget
{
public:
    MyWidget( QWidget *parent=0, const char *name=0 );
};

MyWidget::MyWidget( QWidget *parent, const char *name )
        : QWidget( parent, name )
{
    setMinimumSize( 200, 120 );
    setMaximumSize( 200, 120 );

    QPushButton *quit = new QPushButton( "Quit", this, "quit" );
...            

Let's modify it and leave like this one, so you could rapidly move betwen several objects in memory with the same structure:
...
QPushButton *quit;
QPushButton *quit1;
QPushButton *quit2;
QPushButton *quit2;

class MyWidget : public QWidget
{
public:
    MyWidget( QWidget *parent=0, const char *name=0 );
};

MyWidget::MyWidget( QWidget *parent, const char *name )
        : QWidget( parent, name )
{
    setMinimumSize( 200, 120 );
    setMaximumSize( 200, 120 );

    quit = new QPushButton( "Quit", this, "quit" );
    quit1 = new QPushButton( "Quit", quit, "quit1" );
    quit2 = new QPushButton( "Quit", quit, "quit2" );
    quit3 = new QPushButton( "Quit", quit, "quit3" );            
...         

Can you see the main diferences?, Controls quit, quit1, quit2 and quit3 are now declared for global access (.data section instead of heap) and quit1, quit2 and quit3 have been declared to be childs of widget quit (see QPushButton constructor por more info). Compile the current project (make?).

Disassemble the binary (objdump), and try to locate where in the MyWidget::MyWidget function are created each one of controls quit*.
Possible reference to string:
"Salir"

0804aae4  push   $0x804b1a4
0804aae9  lea    0xfffffffc(%ebp),%eax
0804aaec  push   %eax
0804aaed  mov    %eax,%esi

Reference to function : __7QStringPCc

0804aaef  call   0804a4a4 

Possible reference to string:
"quit"

0804aaf4  push   $0x804b1aa
0804aaf9  push   %edi
0804aafa  push   %esi
0804aafb  push   %ebx

Reference to function : __11QPushButtonRC7QStringP7QWidgetPCc

0804aafc  call   0804a334 
0804ab01  movb   $0x0,0xfffffff7(%ebp)
0804ab05  mov    %eax,0x804c9e0  
0804ab0a  add    $0x30,%esp          
The above code belongs to the statment: quit = new QPushButton( "Salir", this, "quit" );

First, a QString is created for the 'caption' member in the widget at 0x0804aaefh (see previous code listing), later, the QString pointer and the control name are pushed for creating the QPushButton at 0x0804aafch

Debugging time. The goal is to examine de data of the widget after it's creation, to get the real offsets of the parentObj and childOjects members. We'll take care about it's memory layout everytime any of the other quit* controls are being created.

So we break at 0x0804ab0a (see previous code listing) and examine the data at quit (is a global, gdb should work)
quit+0*uint:0x8067a50: 0x2d300a2d  0x08065a40  0xbffff984  0x00000000
quit+4*uint:0x8067a60: 0x00000000  0x00000000  0x00000000  0x00000000
We get at quit+8 the QOject *parentObj pointer, that's where the quit's parent memory layout is located.

Next step is to create the quit1 PushButton, using quit as his parent ( next call to __11QPushButtonRC7QStringP7QWidgetPCc in the disassemble of file ). Once created, the childObjects member of quit object has to include at least 1 item. We'll locate where the pointer is examining quit's layout again:
quit+0*uint:0x8067a50: 0x2d300a2d 0x08065a40 0xbffff984 0x08069f30
quit+4*uint:0x8067a60: 0x00000000 0x00000000 0x00000000 0x00000000
See what's now at quit+0xc (it's supossed to be QobjectList *childOjects):
0x8069f30: 0x00000000 0x404aa4c0 0x08069f58 0x08069f58
0x8069f40: 0x08069f58 0x00000000 0x00000001 0x00000000
Let's break again after next creation, just after call to create quit2, and see what changes on this QObjectList structure:
0x8069f30: 0x00000000 0x404aa4c0 0x08069f58 0x0806a950
0x8069f40: 0x0806a950 0x00000001 0x00000002 0x00000000
By comparing the two dumps, we start our conclusion with this structure:
0x8069f30: Unknown  Methods[]  *firstNode  *lastNode
0x8069f40: *currNode *currIndex *numNodes  *Iterators
If it's correct, the creation of next pushbutton (quit3) must modify the values: numNodes (increased by one), currIdenx (increased by one), currNode (new value) and lastNode (new value is = currNode).

Break at end of quit3 creation and dump again:
0x8069f30: 0x00000000 0x404aa4c0 0x08069f58 0x0806aba8
0x8069f40: 0x0806aba8 0x00000002 0x00000003 0x00000000
Now that we have access to the QobjectList *childObjects list of the widget, let's read a single node, for example the current (currNode) one:
0x806aba8: 0x0806aae0 0x0806a950 0x00000000 0x00000019
We have to stamp then the layout of a QLNode structure:
0x806aba8: Item:0x0806aae0 prev:0x0806a950 next:0x00000000
If we remember the order of the list, numIndex tells that we're on the last widget (as numNodes is 3, we are in node 2 of a 0 indexed list), so there's no next item in the list. Let's dump previous node:
0x806a950: Item:0x0806a888 prev:0x08069f58 next:0x0806aba8
As we can see, the next node is where we came from. Let's go to next Node:
0x8069f58: Item:0x08069e68 prev:0x00000000 next:0x0806a950
Again, next node is where we came from, and now there's no previous node.

All seems to go ok as we have 3 nodes in the list, just the value of numNodes.

And, what the item values are?. Easy to answer.. the values are just the offsets in memory where holds the 3 widgets: quit1, quit2 and quit3:
(gdb output)
(gdb) p /x quit1 = 0x8069e68
(gdb) p /x quit2 = 0x806a888
(gdb) p /x quit3 = 0x806aae0
Let's complete a little the gathered information. Go back to QObject structure and you can see a member called objname (const char *). Lets get the quit2 item from the list, value 0x806a88 and dump to see it's objname pointer:
quit2+0*uint:0x806a888: 0x00000200 0x0806a940 0x08068910 0x00000000
quit2+4*uint:0x806a898: 0x00000000 0x00000000 0x00000000 0x00000000

*string:0x806a940: "quit2"
Now we have the name of the control. objnames are only for debugging porpouses and future qt GUI desing applications as mentioned in qt documentation.

I think this could be enought in research for the qtree application, so let's start programming.


building the tree

Really it's now easy to do, as we can collect all the information from one widget relating to it's parent and childs. The main process must:
  • search for the parent of a widget
    Snipe the memory untill a widget structure can be recognized. From it we can (and indeed we'll need to) read it's parent and childs members.
  • recursive search for the parent of a widget
    and it's parent's untill we found an orphan widget. This will be our new root widget.
  • recursive search all the childs of the new root widget (and each childs')

The most important task here is a successfull read operation from QObjectList *childObjects. For that we assume that the list state is as at generation time. The las item added is the first item in list, and currentNode is the last one. So we can go trough nodes reading the QLNode *prev member untill an invalid one is found.

The rest is how to show collected information, but it's more a programming task then a reverse enginering matter, so it won't be explained here.


qtree, the application

I have also developed an application that do the explained above as part of a (never published and never will) toolset for qt reversing. qtree builds the widget tree from an application acepting several options in the command line.

This is the download link: qtrev-doc-src.tar.gz

It includes a readme file that you must read before blaming in flames about why it doesn't work for you.

In order to work i've assumed a lot as initial state, so operation will success only if (and only):

  • Try to find widget by bgcolor
    By default qt skin, background color of widget is 0x00c0c0c0 (RGB value). So it starts searching the stack (a fixed size window) for that value, as is a common practice to do the main widget a local variable in qt programming. Searched data may be modified using -d option, and window size may be modified also using -s option.

    Starting addres (virtual) may be modified using -s option too. Also -b vaddr option will tell the qtree application that a widget is at that offset (and must be there as no more search will be performed).

    The -p option may force the recursive parent search (it's activated by default but deactivated by option -b).

  • Do recursive find the parent root

    .. by reading the QObject *parentObj member of the qwidget untill a 0x0 is reached as parentObj.

  • Do recursive find all childs

    .. reading the QObjectList *childObjects member. Here the program assumes the list is unmidified from generation time (items are stored in order and currentNode is the last node) searching allways (and only) for previous widgets in the it.
  • * print widget name

    If exists, the option -n will print widget's name.

    Also, -v and -h options are allowed.

Start a qt (version 2.x) application and attach qtree to it's pid. This is the result of one of the examples (the tutorial one, at last stage: t14, see an image here) included in the qt documentation:
locating parent..
[0xbffff978]
+[0x8075888]
 +[0x8075ed8]
 +[0x8075bc0]
+[0x8075068]
+[shotsleftLabel:0x8074dd0]
+[hitsLabel:0x8074b48]
+[shotsleft:0x80748b0]
+[hits:0x8074650]
+[newgame:0x80742e8]
+[shoot:0x8073fb8]
+[cannonFrame:0x80731a0]
 +[cannonField:0x8073650]
 +[cannonFrame:0x8073310]
+[force:0x8072360]
 +[label:0x8072d18]
 +[slider:0x8072ab0]
 +[lcd:0x8072828]
 +[force:0x80724e8]
+[angle:0x8070258]
 +[label:0x80710f8]
 +[slider:0x8070e90]
 +[lcd:0x8070c08]
 +[angle:0x8070458]
+[quit:0x806d1a0]

(I had to resize window (-s700) for a correct search as the value is being found before in the stack) There you can see the names of the widgets and it's offset within the application's memory layout.

This offset is a interesting starting point when you want to modify the application GUI. All widget data (main structure) is stored there, incluiding properties (hidden, enabled, and so) and also callbacks to the widget (when clicked, pressed...), so from here you can go fast in qt reversing an application.
There's another tutorial about that in this site.. the next step.



FYE.. I've included some trees of known applications:

kcalc
lin: 403e6000-40867000 r-xp 00000000 03:03 137233     /usr/lib/libqt.so.2.3.1
badd: 0x403e6000
lib: /usr/lib/libqt.so.2.3.1
forced parent search..
widget at 0x80733e8 has no parent. Assuming root widget..
[0x40044060  : 0x80733e8]
+[QVBoxLayout unnamed : 0x808b068]
 +[QHBoxLayout unnamed : 0x808ae90]
 +[QHBoxLayout unnamed : 0x808acb8]
 +[QHBoxLayout unnamed : 0x808aae0]
 +[QHBoxLayout unnamed : 0x808a940]
+[QWidget unnamed : 0x8080b80]
 +[QGridLayout unnamed : 0x808a528]
 +[QPushButton modbutton : 0x8089e58]
 +[QPushButton OneComplementbutton : 0x8089b40]
 +[QPushButton percentbutton : 0x8089828]
 +[QPushButton equalbutton : 0x8089510]
 +[QPushButton 0button : 0x8089210]
 +[QPushButton periodbutton : 0x8088ef8]
 +[QPushButton shiftbutton : 0x8088bf0]
 +[QPushButton minusbutton : 0x80888e8]
 +[QPushButton plusbutton : 0x80885d0]
 +[QPushButton 3button : 0x80882c0]
 +[QPushButton 2button : 0x8087fb0]
 +[QPushButton 1button : 0x8087ca0]
 +[QPushButton orbutton : 0x80879a0]
 +[QPushButton divisionbutton : 0x8087688]
 +[QPushButton Multiplybutton : 0x8087378]
 +[QPushButton 6button : 0x8087068]
 +[QPushButton 5button : 0x8086d58]
 +[QPushButton 4button : 0x8086a48]
 +[QPushButton andbutton : 0x8086748]
 +[QPushButton parenclosebutton : 0x8086430]
 +[QPushButton parenopenbutton : 0x8086118]
 +[QPushButton 9button : 0x8085e08]
 +[QPushButton 8button : 0x8085af8]
 +[QPushButton 7button : 0x80857e8]
 +[QPushButton ACbutton : 0x80854e8]
 +[QPushButton Clearbutton : 0x80851d0]
 +[QPushButton MCbutton : 0x8084ed0]
 +[QPushButton Mplusminusbutton : 0x8084bb8]
 +[QPushButton MRbutton : 0x80848a8]
 +[QPushButton EEbutton : 0x8084528]
+[QWidget unnamed : 0x80809d0]
 +[QGridLayout unnamed : 0x808a158]
 +[QPushButton Fbutton : 0x8084228]
 +[QPushButton powerbutton : 0x8083f10]
 +[QPushButton lnbutton : 0x8083c00]
 +[QPushButton Ebutton : 0x8083900]
 +[QPushButton squarebutton : 0x80835e8]
 +[QPushButton logbutton : 0x80832d8]
 +[QPushButton Dbutton : 0x8082fd8]
 +[QPushButton factorialbutton : 0x8082cc0]
 +[QPushButton Tanbutton : 0x80829b0]
 +[QPushButton Cbutton : 0x80826b0]
 +[QPushButton recibutton : 0x8082398]
 +[QPushButton Cosbutton : 0x8082088]
 +[QPushButton Bbutton : 0x8081d88]
 +[QPushButton plusminusbutton : 0x8081a70]
 +[QPushButton Sinbutton : 0x8081760]
 +[QPushButton Abutton : 0x8081450]
 +[QPushButton InverseButton : 0x8081140]
 +[QPushButton hypbutton : 0x8080d78]
+[QButtonGroup base : 0x807d280]
 +[QRadioButton unnamed : 0x807fec0]
  +[QAccel buttonAccel : 0x8080428]
 +[QRadioButton unnamed : 0x807f3b0]
  +[QAccel buttonAccel : 0x807f918]
 +[QRadioButton unnamed : 0x807e870]
  +[QAccel buttonAccel : 0x807edd8]
 +[QRadioButton unnamed : 0x807dcf8]
  +[QAccel buttonAccel : 0x807e2a8]
 +[QVBoxLayout unnamed : 0x807d498]
  +[QGridLayout unnamed : 0x807d818]
+[QButtonGroup angle : 0x8079fd8]
 +[QRadioButton unnamed : 0x807c770]
  +[QAccel buttonAccel : 0x807ccd8]
 +[QRadioButton unnamed : 0x807bc60]
  +[QAccel buttonAccel : 0x807c1c8]
 +[QRadioButton unnamed : 0x807b028]
  +[QAccel buttonAccel : 0x807b5c8]
 +[QVBoxLayout unnamed : 0x807a238]
  +[QGridLayout unnamed : 0x807a8d8]
+[QLabel ERROR : 0x8079d78]
+[QLabel HYP : 0x8079b18]
+[QLabel INV : 0x80798d8]
+[0x40044500 display : 0x8078ec0]
+[QPushButton helpbutton : 0x8078bb8]
+[QPushButton configbutton : 0x8077518]
+[QAccel unnamed : 0x8075628]
and also kmix
lin: 403ba000-4083b000 r-xp 00000000 03:03 137233     /usr/lib/libqt.so.2.3.1
badd: 0x403ba000
lib: /usr/lib/libqt.so.2.3.1
[0x8064900  : 0x80a4570]
+[QSlider RightLeft : 0x80e1cf0]
+[0x80651a0 Video : 0x80dd110]
 +[0x4037cfa0 Keys : 0x80e0f68]
 +[QTimer unnamed : 0x80e0da0]
 +[0x8064f80 RecordLED : 0x80e0980]
 +[QSlider Video : 0x80e04a0]
 +[QSlider Video : 0x80dff98]
 +[0x8064f80 MuteLED : 0x80df8a8]
 +[QLabel unnamed : 0x80df488]
 +[QLabel unnamed : 0x80def88]
 +[QVBoxLayout unnamed : 0x80decf0]
  +[QHBoxLayout unnamed : 0x80dfd50]
 +[0x40203260  : 0x80dd368]
  +[0x40203e20 keys : 0x80de978]
  +[0x40203d20 recsrc : 0x80de598]
  +[0x40203e20 show_all : 0x80de220]
  +[0x40203d20 mute : 0x80ddec8]
  +[0x40203e20 hide : 0x80ddb68]
  +[0x40203d20 stereo : 0x80dd778]
+[0x80651a0 PhoneOut : 0x80d92f8]
 +[0x4037cfa0 Keys : 0x80dc430]
 +[QTimer unnamed : 0x80dc2b8]
 +[QSlider PhoneOut : 0x80dbdb0]
 +[0x8064f80 MuteLED : 0x80db6c0]
 +[QLabel unnamed : 0x80db2a0]
 +[QLabel unnamed : 0x80dada0]
 +[QVBoxLayout unnamed : 0x80dab08]
  +[QHBoxLayout unnamed : 0x80dbb68]
 +[0x40203260  : 0x80d9550]
  +[0x40203e20 keys : 0x80da688]
  +[0x40203e20 show_all : 0x80da408]
  +[0x40203d20 mute : 0x80da0b0]
  +[0x40203e20 hide : 0x80d9d50]
  +[0x40203d20 stereo : 0x80d9960]
+[0x80651a0 PhoneIn : 0x80d4c30]
 +[0x4037cfa0 Keys : 0x80d85a8]
 +[QTimer unnamed : 0x80d83e0]
 +[0x8064f80 RecordLED : 0x80d7fc0]
 +[QSlider PhoneIn : 0x80d7ab8]
 +[0x8064f80 MuteLED : 0x80d73c8]
 +[QLabel unnamed : 0x80d6fa8]
 +[QLabel unnamed : 0x80d6aa8]
 +[QVBoxLayout unnamed : 0x80d6810]
  +[QHBoxLayout unnamed : 0x80d7870]
 +[0x40203260  : 0x80d4e88]
  +[0x40203e20 keys : 0x80d6498]
  +[0x40203d20 recsrc : 0x80d60b8]
  +[0x40203e20 show_all : 0x80d5d40]
  +[0x40203d20 mute : 0x80d59e8]
  +[0x40203e20 hide : 0x80d5688]
  +[0x40203d20 stereo : 0x80d5298]
+[0x80651a0 Line1 : 0x80d0088]
 +[0x4037cfa0 Keys : 0x80d3ee0]
 +[QTimer unnamed : 0x80d3d18]
 +[0x8064f80 RecordLED : 0x80d38f8]
 +[QSlider Line1 : 0x80d3418]
 +[QSlider Line1 : 0x80d2f10]
 +[0x8064f80 MuteLED : 0x80d2820]
 +[QLabel unnamed : 0x80d2400]
 +[QLabel unnamed : 0x80d1f00]
 +[QVBoxLayout unnamed : 0x80d1c68]
  +[QHBoxLayout unnamed : 0x80d2cc8]
 +[0x40203260  : 0x80d02e0]
  +[0x40203e20 keys : 0x80d18f0]
  +[0x40203d20 recsrc : 0x80d1510]
  +[0x40203e20 show_all : 0x80d1198]
  +[0x40203d20 mute : 0x80d0e40]
  +[0x40203e20 hide : 0x80d0ae0]
  +[0x40203d20 stereo : 0x80d06f0]
+[0x80651a0 IGain : 0x80cb4e0]
 +[0x4037cfa0 Keys : 0x80cf338]
 +[QTimer unnamed : 0x80cf170]
 +[0x8064f80 RecordLED : 0x80ced50]
 +[QSlider IGain : 0x80ce870]
 +[QSlider IGain : 0x80ce368]
 +[0x8064f80 MuteLED : 0x80cdc78]
 +[QLabel unnamed : 0x80cd858]
 +[QLabel unnamed : 0x80cd358]
 +[QVBoxLayout unnamed : 0x80cd0c0]
  +[QHBoxLayout unnamed : 0x80ce120]
 +[0x40203260  : 0x80cb738]
  +[0x40203e20 keys : 0x80ccd48]
  +[0x40203d20 recsrc : 0x80cc968]
  +[0x40203e20 show_all : 0x80cc5f0]
  +[0x40203d20 mute : 0x80cc298]
  +[0x40203e20 hide : 0x80cbf38]
  +[0x40203d20 stereo : 0x80cbb48]
+[0x80651a0 CD : 0x80c6818]
 +[0x4037cfa0 Keys : 0x80ca790]
 +[QTimer unnamed : 0x80ca5c8]
 +[0x8064f80 RecordLED : 0x80ca1a8]
 +[QSlider CD : 0x80c9cc8]
 +[QSlider CD : 0x80c97c0]
 +[0x8064f80 MuteLED : 0x80c90d0]
 +[QLabel unnamed : 0x80c8cb0]
 +[QLabel unnamed : 0x80c8690]
 +[QVBoxLayout unnamed : 0x80c83f8]
  +[QHBoxLayout unnamed : 0x80c9578]
 +[0x40203260  : 0x80c6a70]
  +[0x40203e20 keys : 0x80c8080]
  +[0x40203d20 recsrc : 0x80c7ca0]
  +[0x40203e20 show_all : 0x80c7928]
  +[0x40203d20 mute : 0x80c75d0]
  +[0x40203e20 hide : 0x80c7270]
  +[0x40203d20 stereo : 0x80c6e80]
+[0x80651a0 Microphone : 0x80c2008]
 +[0x4037cfa0 Keys : 0x80c5ac8]
 +[QTimer unnamed : 0x80c5900]
 +[0x8064f80 RecordLED : 0x80c54e0]
 +[QSlider Microphone : 0x80c4fd8]
 +[0x8064f80 MuteLED : 0x80c48e8]
 +[QLabel unnamed : 0x80c44c8]
 +[QLabel unnamed : 0x80c3e80]
 +[QVBoxLayout unnamed : 0x80c3be8]
  +[QHBoxLayout unnamed : 0x80c4d90]
 +[0x40203260  : 0x80c2260]
  +[0x40203e20 keys : 0x80c3870]
  +[0x40203d20 recsrc : 0x80c3490]
  +[0x40203e20 show_all : 0x80c3118]
  +[0x40203d20 mute : 0x80c2dc0]
  +[0x40203e20 hide : 0x80c2a60]
  +[0x40203d20 stereo : 0x80c2670]
+[0x80651a0 Line : 0x80bd460]
 +[0x4037cfa0 Keys : 0x80c12b8]
 +[QTimer unnamed : 0x80c10f0]
 +[0x8064f80 RecordLED : 0x80c0cd0]
 +[QSlider Line : 0x80c07f0]
 +[QSlider Line : 0x80c02e8]
 +[0x8064f80 MuteLED : 0x80bfbf8]
 +[QLabel unnamed : 0x80bf7d8]
 +[QLabel unnamed : 0x80bf2d8]
 +[QVBoxLayout unnamed : 0x80bf040]
  +[QHBoxLayout unnamed : 0x80c00a0]
 +[0x40203260  : 0x80bd6b8]
  +[0x40203e20 keys : 0x80becc8]
  +[0x40203d20 recsrc : 0x80be8e8]
  +[0x40203e20 show_all : 0x80be570]
  +[0x40203d20 mute : 0x80be218]
  +[0x40203e20 hide : 0x80bdeb8]
  +[0x40203d20 stereo : 0x80bdac8]
+[0x80651a0 Speaker : 0x80b9528]
 +[0x4037cfa0 Keys : 0x80bc780]
 +[QTimer unnamed : 0x80bc608]
 +[QSlider Speaker : 0x80bc100]
 +[0x8064f80 MuteLED : 0x80bba10]
 +[QLabel unnamed : 0x80bb5f0]
 +[QLabel unnamed : 0x80bafd0]
 +[QVBoxLayout unnamed : 0x80bad38]
  +[QHBoxLayout unnamed : 0x80bbeb8]
 +[0x40203260  : 0x80b9780]
  +[0x40203e20 keys : 0x80ba8b8]
  +[0x40203e20 show_all : 0x80ba638]
  +[0x40203d20 mute : 0x80ba2e0]
  +[0x40203e20 hide : 0x80b9f80]
  +[0x40203d20 stereo : 0x80b9b90]
+[0x80651a0 Pcm : 0x80b5110]
 +[0x4037cfa0 Keys : 0x80b8848]
 +[QTimer unnamed : 0x80b86d0]
 +[QSlider Pcm : 0x80b81f0]
 +[QSlider Pcm : 0x80b7ce8]
 +[0x8064f80 MuteLED : 0x80b75f8]
 +[QLabel unnamed : 0x80b71d8]
 +[QLabel unnamed : 0x80b6bb8]
 +[QVBoxLayout unnamed : 0x80b6920]
  +[QHBoxLayout unnamed : 0x80b7aa0]
 +[0x40203260  : 0x80b5368]
  +[0x40203e20 keys : 0x80b64a0]
  +[0x40203e20 show_all : 0x80b6220]
  +[0x40203d20 mute : 0x80b5ec8]
  +[0x40203e20 hide : 0x80b5b68]
  +[0x40203d20 stereo : 0x80b5778]
+[0x80651a0 Volume : 0x80a7618]
 +[0x4037cfa0 Keys : 0x80b41e0]
 +[QTimer unnamed : 0x80b4068]
 +[QSlider Volume : 0x80b3b88]
 +[QSlider Volume : 0x80b31f8]
 +[0x8064f80 MuteLED : 0x80b27a8]
 +[QLabel unnamed : 0x80b2388]
 +[QLabel unnamed : 0x80b1618]
 +[QVBoxLayout unnamed : 0x80b1380]
  +[QHBoxLayout unnamed : 0x80b2fb0]
 +[0x40203260  : 0x80afe78]
  +[0x40203e20 keys : 0x80b0f00]
  +[0x40203e20 show_all : 0x80b0c80]
  +[0x40203d20 mute : 0x80b0928]
  +[0x40203e20 hide : 0x80a2e50]
  +[0x40203d20 stereo : 0x80a29f0]
+[QVBoxLayout unnamed : 0x80a4d18]
 +[QHBoxLayout unnamed : 0x80a89d8]
+[0x40203260  : 0x80a3dc8]
 +[0x40203e20 show_all : 0x80a8598]