Decoding AMBE+2 in MD380 Firmware in Linux
Decoding AMBE+2 in MD380 Firmware in Linux
38
can easily export symbols by script from IDA Pro
1 // Load an SRAM image .
i n t fdram=open ( "ram . b i n " , 0 ) ;
or Radare2.
3 void ∗ sram=mmap( The symbol file is just a collection of assignments
( void ∗ ) 0 x20000000 , of names to addresses in roughly C syntax.
5 ( s i z e _ t ) 0 x20000 ,
PROT_EXEC|PROT_READ|PROT_WRITE, /∗ P o p u l a t e s t h e a u d i o b u f f e r ∗/
7 MAP_PRIVATE, // f l a g s 2 ambe_decode_wav = 0 x08051249 ;
fdram , // f i l e /∗ J u s t r e t u r n s . ∗/
9 0 // o f f s e t 4 n u l l s u b = 0 x08098e15 ;
);
11
// C r e a t e an empty TCRAM r e g i o n .
13 i n t f d t c r a m=open ( " / dev / z e r o " , 0 ) ;
You can include it in the executable by passing
void ∗ tcram=mmap( GCC parameters to the linker, or by calling ld di-
15 ( void ∗ ) 0 x10000000 , rectly.
( s i z e _ t ) 0 x10000 ,
17 PROT_READ|PROT_WRITE, // p r o t e c t i o n s CC=arm−l i n u x −gnueabi−gcc −6 − s t a t i c −g
MAP_PRIVATE, // f l a g s 2 $ (CC) −o t e s t t e s t . c \
19 fdtcram , // f i l e −X l i n k e r −−j u s t −symbols=symbols
0 // o f f s e t
21 );
Now that we can load the firmware into process
memory and call its functions, let’s take a step back
and see a second way to do the linking, by rewrit-
ing the firmware dump into an ELF object and then
5.2 Symbol Imports linking it. After that, we’ll get along to decoding
Now that we’ve got the code loaded, calling it is as some audio.
simple as calling any other function, except that our
C program doesn’t yet know the symbol addresses. 5.3 Static Firmware Linking
There are two ways around this:
While it’s nice and easy to load firmware with
The quick but dirty solution is to simply cast a mmap(2) at runtime, it would be nice and correct
data or function pointer. For a concrete example, to convert the firmware dump into an object file for
there is a null function at 0x08098e14 that sim- static linking, so that our resulting executable has
ply returns without doing anything. Because it’s no external dependencies at all. This requires both
a Thumb function and not an ARM function, we’ll a bit of objcopy wizardry and a custom script for
have to add one to that address before calling it at ld.
0x08098e15. First, let’s convert our firmware image dump to
void ( ∗ n u l l s u b ) ( ) =(void ∗ ) 0 x08098e15 ; an ELF that loads at the proper address.
2
1 arm−l i n u x −gnueabi−o b j c o p y \
p r i n t f ( " Trying t o c a l l n u l l s u b ( ) . \ n" ) ;
−I b i n a r y e x p e r i m e n t . img \
4 nullsub () ;
3 −−change−a d d r e s s e s =0x0800C000 \
p r i n t f ( " S u c c e s s ! \ n" ) ;
−−rename−s e c t i o n . data =. e x p e r i m e n t \
5 −O e l f 3 2 −l i t t l e a r m −B arm e x p e r i m e n t . o
39
5.4 Decoding the Audio
1 // P l a c e d a t 0 x08051249
To decode the audio, I decided to begin with the i n t ambe_decode_wav (
3 signed short ∗ w a v b u f f e r ,
same .amb format that DSD uses. This way, I could signed i n t e i g h t y , // a l w a y s 80
work from their reference files and compare my de- 5 short ∗ b i t b u f f e r , // 0 x20011c8e
coding to theirs. i n t a4 , // 0
The .amb format consists of a four byte header 7 short a5 , // 0
short a6 , // t i m e s l o t , 0 or 1
(2e 61 6d 62) followed by eight-byte frames. Each 9 i n t a7 // 0 x20011224
frame begins with a zero byte and is followed by );
49 bits of data, stored most significant bit first with
the final bit in the least significant bit of its own
byte. For any parameter that I don’t understand, I
To have as few surprises as possible, I take the just copy the value that I’ve seen called through my
eight packed bytes and extract them into an array of hooks in the firmware running on real hardware. For
49 shorts located at 0x20011c8e, because this is the example, 0x20011224 is some structure used by the
address that the firmware uses to store its buffer. AMBE code, but I can simply re-use it thanks to
Shorts are used for convenience in addressing dur- my handy RAM dump.
ing computation, even if they are a bit more verbose Since everything is now in the right position, we
than they would be in a convenient calling conven- can decode a frame of AMBE to two audio frames
tion. in quick succession.
1 //Re−u s e t h e f i r m w a r e ’ s own AMBE b u f f e r .
//One AMBE frame becomes two a u d i o frames .
short ∗ambe=(short ∗ ) 0 x 2 0 0 1 1 c 8 e ;
2 ambe_decode_wav (
3
outbuf0 , 8 0 , ambe ,
i n t ambei =0;
4 0, 0, 0,
5 f o r ( i n t i =1; i <7; i ++){ // S k i p f i r s t b y t e .
0 x20011224
f o r ( i n t j =0; j <8; j ++){
6 );
7 //MSBit f i r s t
ambe_decode_wav (
ambe [ ambei++]=(packed [ i ]>>(7− j ) ) &1;
8 ou tbuf1 , 8 0 , ambe ,
9 }
0, 0, 1,
}
10 0 x20011224
11 // F i n a l b i t i n i t s own frame as LSBit .
);
ambe [ ambei++]=packed [ 7 ] & 1 ;
Additionally, I re-use the output buffers to store After dumping these to disk and converting to
the resulting WAV audio. In the MD380, there are a .wav file with sox -r 8000 -e signed-integer
two buffers of audio produced from each frame of -L -b 16 -c 1 out.raw out.wav, a proper audio
AMBE. file is produced that is easily played. We can now
decode AMBE in Linux!
// 80 s a m p l e s f o r each a u d i o b u f f e r
2 short ∗ o u t b u f 0 =(short ∗ ) 0 x20011aa8 ;
short ∗ o u t b u f 1 =(short ∗ ) 0 x20011b48 ;
41
5.5 Runtime Hooks 5.6 I/O Traps
So now we’re able to decode audio frames, but this is What about those I/O functions that we’ve forgot-
firmware, and damned near everything of value ex- ten to hook, or ones that have been inlined to a
cept the audio routines will eventually call a function dozen places that we’d rather not hook? Wouldn’t
that deals with I/O—a function we’d better replace it sometimes be easier to trap the access and fake
if we don’t want to implement all of the STM32’s the result, rather than hooking the same function?
I/O devices. You’re in luck! Because this is Unix, we can sim-
ply create a handler for SIGSEGV, much as Jeffball
Luckily, hooking a function is nice and easy. We
did in PoCkGTFO 8:8. Your segfault handler can
can simply scan through the entire image, replac-
then fake the action of the I/O device and return.
ing all BX (Branch and eXchange) instructions to
Alternately, you might not bother with a proper
the old functions with ones that direct to the new
handler. Instead, you can use GDB to debug the
functions. False positives are a possibility, but we’ll
process, printing a backtrace when the I/O region
ignore them for now, as the alternative would be to
at 0x40000000 is accessed. While GDB in Qemu
list every branch that must be hooked.
doesn’t support ptrace(2), it has no trouble trap-
The BL instruction in Thumb is actually two ad- ping out the segmentation fault and letting you
jacent 16-bit instructions, which load a low and high know which function attempted to perform I/O.
half of the address difference into the link register,
then BX against that register. (This clobbers the
link register, but so does any BL, so the register use 5.7 Conclusion
is effectively free.) Thank you kindly for reading my ramblings about
ARM firmware. I hope that you will find them
1 /∗ C a l c u l a t e s Thumb code t o branch from
one a d d r e s s t o a n o t h e r . ∗/
handy in your own work, whenever you need to work
3 i n t c a l c b l ( i n t adr , i n t t a r g e t ) { with reverse engineered firmware away from its own
/∗ Begin w i t h t h e d i f f e r e n c e o f t h e t a r g e t hardware.
5 and t h e PC, which p o i n t s t o j u s t a f t e r If you’d like to similarly instrument Linux ap-
t h e c u r r e n t i n s t r u c t i o n . ∗/
7 i n t o f f s e t=t a r g e t −adr −4;
plications, take a look at Jonathan Brossard’s
// LSBit doesn ’ t c o u n t . Witchcraft Compiler Collection,15 an interactive
9 o f f s e t =( o f f s e t >>1) ; ELF shell that makes it nice and easy to turn an
executable into a linkable library.
11 /∗ The BL i n s t r u c t i o n i s a c t u a l l y two
Thumb i n s t r u c t i o n s , w i t h one s e t t i n g The emulator from this article has now been in-
13 t h e h i g h p a r t o f t h e LR and t h e o t h e r corporated into my md380tools16 project, for use in
s e t t i n g t h e low p a r t w h i l e swapping Linux.
15 LR and PC. ∗/
i n t h i =0xF000 | ( ( o f f s e t &0xFFF800 ) >>11) ;
17 i n t l o =0xF800 | ( o f f s e t &0x7FF ) ; Cheers from Varaždin, Croatia,
–Travis 6A/KK4VCZ
19 // Return t h e p a i r as a s i n g l e 32− b i t word .
return ( l o <<16) | h i ;
21 }
42