First x86 contact

My hopes to offload as much work as possible were shattered as time was running out and I wasn’t finding the right tool for the job that would be both easy, efficient and not require too much investment from me. If decompilation is not a silver bullet, then there’s no getting around learning some assembly to understand the target code and figure what the original source code may have looked like. Once you have working code, you may be happy and move on to using it but I’m way too curious to not try to also understand how things work beyond mechanically turning a byte stream into a different byte stream.

They say curiosity killed the cat, so I’m probably a serial pet killer at this point and have always been. Of course it’s not my fault, it never is. I can easily shift the blame to public school that gave me the taste to learn and the lack of responsible adults when I was a kid to tell me how I would endanger an entire feline species. But I digress…

So soon after I discovered programming, I tried to learn more about computers in general and eventually I got my virtual hands onto a file called nasm.chm that would keep me occupied for a while. I would mainly write small useless programs using a handful of x86 and x87 instructions to compute well known functions using their Taylor series approximation.

Learning x86

To be honest, I don’t remember whether I was using nasm or fasm, but either way it came with a CHM file: a web based self-contained file with an index and the possibility to jump between sections via hyper links. You’d even find trivia like how many CPU cycles each instruction would take.

These days I’m way more comfortable with the man utility, but in a sense CHM can be viewed as a GUI alternative to GNU info. While I understand the rationale and terminal appeal behind info, I’m always confused whenever I need to look up something not mentioned in the standard manual. In that CHM file I would easily find everything and that probably played a part in the learning process, but I digress.

So let’s look at a very small and simple function:

10001110: push   %esi
10001111: mov    0x8(%esp),%esi
10001115: test   %esi,%esi
10001117: jne    0x1000111d
10001119: xor    %eax,%eax
1000111b: pop    %esi
1000111c: ret
1000111d: push   %edi
[...]
1000113f: ret

This looks nothing like what I learned about x86 assembly. Well it does look familiar but I don’t understand the notation. It turns out this is the AT&T notation and even though I’ve been living on the UNIX-like side of the fence for years now I’ll stick to the Intel notation:

objdump --disassemble-all --disassembler-options=intel xadec.dll

Much better, I can now start my mental decompilation:

10001110: push   esi                      ; esp offset to eip is now 4
10001111: mov    esi,DWORD PTR [esp+0x8]  ; esi = arg0
10001115: test   esi,esi                  ; if (arg0 == NULL) {
10001117: jne    0x1000111d               ;     /* else jump */
10001119: xor    eax,eax                  ;     eax = 0
1000111b: pop    esi                      ;     esp offset to eip is now 0
1000111c: ret                             ;     return (0);
                                          ; }
1000111d: push   edi                      ; esp offset to eip is now 8
[...]
1000113f: ret

Isn’t it funny that physicists accidentally called atoms as such because they were thought to be indivisible? It turns out they are made of electrons, protons and neutrons. And we can even break some further down to what we now think are elementary particles although at this point, I digress…

I was lucky that xadec.dll contained only one instruction I had never encountered before, so the learning curve was almost flat. Instructions are like atoms, the smallest decomposition of work we can give an x86-compatible processor although depending on the operands they will yield different opcodes. The opcodes and their argument bytes don’t bring any value to the objdump output so I cut them out.

Prior exposure to x86 code helped a lot. This way I knew immediately that xor eax, eax is a common alternative to mov eax, 0 and wasn’t bewildered that esi would be tested against itself. Why though, I don’t know, but I suspect this may simply be more efficient or result in smaller machine code. I may be curious, but I can also enjoy a bit of mystery.

However, how can I tell that arg0 is a pointer and not simply a scalar? How can I assume that this is a null check?

The first clues

The snippets above are from the xaDecodeClose function, one of the 4 public symbols I can find in the disassembly:

Export Address Table -- Ordinal Base 1
        [   0] +base[   1] 1110 Export RVA
        [   1] +base[   2] 1170 Export RVA
        [   2] +base[   3] 1000 Export RVA
        [   3] +base[   4] 1140 Export RVA

[Ordinal/Name Pointer] Table
        [   0] xaDecodeClose
        [   1] xaDecodeConvert
        [   2] xaDecodeOpen
        [   3] xaDecodeSize

Looking at other properties of the file I can make sense of the address 1110 of the first table, and deduce that xaDecodeClose is located at 10001110:

BaseOfCode              00001000
BaseOfData              00006000
ImageBase               10000000

All public symbols seem to land in the code section of the library. The C code generated by retdec also confirms my amazing deduction of the location of xaDecodeClose with my mighty arithmetic powers:

// Address range: 0x10001110 - 0x1000113f
int32_t xaDecodeClose(char * pMem, int32_t a2) {
    // 0x10001110
    if (pMem == NULL) {
        // 0x10001119
        return 0;
    }
    // 0x1000111d
    GlobalUnlock(GlobalHandle(pMem));
    GlobalFree(GlobalHandle((char *)(int32_t)pMem));
    return 1;
}

I said earlier that retdec produces horrible C code, this is quite readable. Don’t be fooled though, because retdec was confused and got the signature wrong. It failed to figure the calling convention of the library but that didn’t bother me too much, I knew.

How did I know that? And how did I know that arg0 is a pointer prior to peeping at the decompiled C?

Open source to the rescue, again!

Even though there’s no source code for xadec.dll, it has been used by more than one project so they need to know how to use it. Thanks to DTXMania being open source, I not only found the bundled DLL in the source tree but also a xadec.h file and even better: a sample.c file.

After wasting a fair amount of time failing to cross-compile sample.c to run it in Wine, hoping to get a sample output to compare to what my code would provide, I gave up. I had everything: xadec.dll, xadec.lib, xadec.h, a tool chain to build Windows binaries and Wine to run sample.exe but it would always fail to link and at some point I ran out of the 2-hour budget I had allocated for that. I realized later that it wouldn’t be a problem.

Not too long ago, we discovered that one of our fellow Varnish developers is a beekeeper on his spare time. That reminded me of an old joke of mine that for some reason never caught on: API culture. That was supposed to be a pun: “I’m doing apiculture” was suppose to mean “I’m designing the API”. In French “apiculture” means beekeeping. It’s a mystery why this joke never caught on.

Anyway, thanks again to DTXMania I could sum up the API to this:

typedef struct _XASTREAMHEADER { ... } XASTREAMHEADER;
typedef struct _XAHEADER { ... } XAHEADER;
typedef HANDLE HXASTREAM;

HXASTREAM __cdecl xaDecodeOpen(XAHEADER *, WAVEFORMATEX *);
BOOL __cdecl xaDecodeClose(HXASTREAM);
BOOL __cdecl xaDecodeSize(HXASTREAM, ULONG, ULONG *);
BOOL __cdecl xaDecodeConvert(HXASTREAM, XASTREAMHEADER *);

This is a gold mine:

  • 2 structure definitions
  • 1 opaque structure
  • 4 function signatures
  • 1 calling convention

After a quick MSDN search and a quick look at other Win32 functions used in xaDecodeOpen I was able to decompile the function:

void
xadec_close(struct xahandle *hdl)
{

	free(hdl);
}

My actual code doesn’t look like that, but in essence I don’t care about the return value and free(3) already does a null check for me. I can now move to the next low-hanging fruit because thanks to my past self I didn’t need more research before proceeding. In the next post I will discuss audio file formats and the WAVE file format in particular.