PS: most of the research is collaboration from I and a very good researcher Fabien Ansglard whose done most of the coding you see here)
Now as I explained theres 3 computers (systems) on a 'smart' phone , well
-----The Smart (read in this context ANDROID/iOS)
-----The Phone (read in this context Baseband)
-----The SIM (duuuh)
now, superbly we have two phones that we love to break into (kernel level) by rooting and jailbreaking that is android and iOS respectively,
so we go for iOS first, we break down the structures:
A kernel module exposes the serial line over an UNIX pseudo-terminal in the
/dev
folder. On Androids there is only one pseudo terminal: /dev/smd0
but on iPhone the UART is divided by a kernel module and several pseudo-terminals are exposed: /dev/mux.h5-baseband.reg
, /dev/dlci.h5-baseband.call
or/dev/dlci.h5-baseband.sms
. The user land process can then open
any terminal and perform I/O commands with simple read
and write
.
then here we go to android:
now this is the method most unlocks rely on, you get inside the baseband and check how a specific model allows certain MNC & MCCs , now, since we are not really going for that, we just wanna play MITM between the OS and the modem OS control what goes into the phone and into the OS :)
How libraries are loaded (theory)
How libraries are loaded (theory)
In MacOS X/Linux, programs are stored on hard-drive with missing parts referencing methods and functions from bigger libraries (libc,zlib,...). Only when they are started the missing symbols are linked, it is called dynamic linking and it's done by ld on Linux and dyld on MacOS X:
- After a new process is forked an execv occurs, the program is read from hard-drive and different sections are loaded in RAM, .data is loaded into "read only" pages while .text and a stack is created with "read and write" pages.
- Note: In the drawing .data's pages are mapped starting at 0x00000000 but in reality this is reserved so you get a nice "segmentation fault" upon de-refencing a null pointer.
- Once the program's different section are all in pages, the kernel reads which loader should be used and integrate it in the process address space. The loader is usually already resident in memory somewhere on the system so it is not loaded from hard-drive but mapped by adding an entry in the progress's page table. Execution control is them transferred to the loader, passing via parameters where the different program sections are.
- The loader reads the missing symbols names and search for them in the default libraries.
- The libraries are usually also resident in memory so there is no need to read them from the hard-drive. They are mapped in the process address space via the process's page table and symbols resolution occurs. If all symbols are resolved, execution of the program can begin.
Note that the loader is performing symbols resolution (intercepted by our library) at launchtime. The interceptor library then uses the loader to create a hook atruntime.
It is not complicated to do:
Of course to make the program run you also have to get a hold on the "real" function via dlsym(RTLD_NEXT, "malloc") and relay the call so everything is transparent to the program.
Dummy source code (malloc_interposer.c):
Execution :
By placing a special sub-section __interpose in the data portion of the executable, dyld will perform all the interceptions automatically. Here is an example hooking open,close,write and read.
now i will put up part2 where i will give the code capable of MITM
It is not complicated to do:
- Write a library with function prototype matching the one you want to intercept (let's say: void *malloc(size_t size))
- Compile as a shared library
- Instruct the loader to lookup this library before anything else:
- On linux this is done via the LD_PRELOAD environment variable.
- On MacOS X it is slighty different because lyld uses a two-level namespaces ( a symbol not only features a method's name but also the name of the library) hence you have to specify the name of your interceptor library via DYLD_INSERT_LIBRARIES but also instruct lyld to use a flat lookup system via DYLD_FORCE_FLAT_NAMESPACE
Of course to make the program run you also have to get a hold on the "real" function via dlsym(RTLD_NEXT, "malloc") and relay the call so everything is transparent to the program.
Dummy source code (malloc_interposer.c):
#include <stdio.h> #include <dlfcn.h> #include <sys/types.h> #include <sys/stat.h> void *malloc(size_t size) { static void * (*func)(); if(!func) func = dlsym(RTLD_NEXT, "malloc"); printf("malloc(%d) is called\n", size); return(func(size)); }
Compilation :
$: gcc -D_GNU_SOURCE -rdynamic -shared malloc_interposer.c -o /lib/malloc_interposer.so.1.0 -ldl
$: LD_PRELOAD=/lib/malloc_interposer.so.1.0 cat /dev/null malloc(20) is called
Library preloading & Method interposition: Practice
While the method described previously works very well on Linux, MacOS X tend to behave poorly when you flatten the lookup system of lyld. Luckily there is an other way to place a hook on MacOS X and this method is described in Amit Singh's gem: MacOS X Internals as method interposition:By placing a special sub-section __interpose in the data portion of the executable, dyld will perform all the interceptions automatically. Here is an example hooking open,close,write and read.
static const interposer_t interposers[] __attribute__ ((section("__DATA, __interpose")))=
{
{ (void*)my_open, (void*)open },
{ (void*)my_close, (void*)close},
{ (void*)my_read, (void*)read},
{ (void*)my_write, (void*)write},
};
int my_open (const char* path, int flags, mode_t mode){..}
int my_close (int d){..}
int my_read (int handle, void *buffer, int nbyte ){..}
int my_write (int handle, void *buffer, int nbyte ){..}
With this trick, it was easy to identify the pseudo-terminals used by placing a hook on open and close. Then hook read and write. The tracing is performed by maintaining a mapping between file descriptor returned by <fcntl.h> and FILE*'s . Here is the resulting source code: fdinterceptor.c and a zip containing a plist and the script to inject: inject.zip.
Toolchain in action:
Toolchain in action:
// Build the tools $ cd /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin $ gcc-4.2 -arch armv6 -dynamiclib -isysroot ../../SDKs/iPhoneOS3.1.3.sdk -o fdinterceptor.dylib fdinterceptor.c // Send the tools $ scp fdinterceptor.dylib injectCommCenter.sh com.apple.CommCenter.plist root@192.168.1.103:/tmp // Jump in and inject $ ssh -l root 192.168.1.103 # cd /tmp # ./injectCommCenter.sh
Notice that injection is performed via a script
injectCommCenter.sh
: cd /System/Library/LaunchDaemons/
cp com.apple.CommCenter.plist com.apple.CommCenter.plist.vanilla
cp /tmp/com.apple.CommCenter.plist /System/Library/LaunchDaemons/com.apple.CommCenter.plist
launchctl unload -w /System/Library/LaunchDaemons/com.apple.CommCenter.plist
launchctl load -w /System/Library/LaunchDaemons/com.apple.CommCenter.plist
cp com.apple.CommCenter.plist.vanilla com.apple.CommCenter.plist
The launchctl lines are not very interesting as they merely unload and reload the CommCenter deamon. But what is done before and afer is a bit more worth mentioning: Because the CommCenter not only handles the modem but also the WIFI connection, once you unload the CommCenter your SSH terminal will HANG. You are literally sawing off the branch you are sitting on. It is hence a necessity to script the re-loading....but there is more:
Because we modified the plist and it is saved on hard-drive: if we have a bug in our interceptor library we may potentially brick the device and require a full DFU restore ! So in order to take into account a worse case scenario the script also remove the interceptor library from the plist, this way the device can restart safely: This is just an idiot proof security.
Results:
Booting
Because we modified the plist and it is saved on hard-drive: if we have a bug in our interceptor library we may potentially brick the device and require a full DFU restore ! So in order to take into account a worse case scenario the script also remove the interceptor library from the plist, this way the device can restart safely: This is just an idiot proof security.
Results:
Booting
[send] at # Modem Are you there ?
[send] at
[send] at
[send] at
[recv] AT # Yes I am !
[send] ate0 # Set modem to "no echo" mode
[recv] ate0 OK
[send] at+cmee=1 # Require error code to be returned as code (opposed to verbose at+cmee=2)
[recv] OK
[send] at+ipr=750000 # Set the terminal speed
[recv] OK
[send] at+xdrv=0,41,25 # Call method 41 on device 0 (speakers)
[recv] +XDRV: 0,41,1,0
[recv] OK
RV: 0,41,1,0
[send] at+xtransportmode # Switch to binary code instead of commands
[recv] OK
[send] at+cscs="HEX" # Set the TE character set to HEX
[recv] OK
[send] at+xthumb?
[recv] +XTHUMB: "1E2834B6CE739AB36EF9454B7997FCD30208398C","E93B43F3EF6DAED516A2D4B9BAD5494DC81E92D3"
[recv] OK
[send] at+xgendata # Request modem's firmware description
[recv] +XGENDATA: "","DEV_ICE_MODEM_04.05.04_G","EEP_VERSION:208","EEP_REVISION:1","BOOTLOADER_VERSION:3.9_M3S2"
[recv] OK
[send] at+xdrv=10,2 # Call a function for a device, format is at+xdrv:deviceId,functionId,params ...
[recv] :+XDRV: 10,2,0
[recv] OK
[send] at+xl1set="psvon"
[send] at+cmux=0,0,0,1500 # Set the multiplexing mode
[recv] OK
[open] '/dev/dlci.h5-baseband.call'
[open] '/dev/dlci.h5-baseband.reg'
[open] '/dev/dlci.h5-baseband.sms'
[open] '/dev/dlci.h5-baseband.low'
[open] '/dev/dlci.h5-baseband.pdp_ctl'
[open] '/dev/dlci.h5-baseband.chatty'
[open] '/dev/dlci.h5-baseband.pdp_0'
[open] '/dev/dlci.h5-baseband.pdp_1'
# The rest of the registration occurs in /dev/dlci.h5-baseband.reg
# The two main used pseudo terminal after this are of course /dev/dlci.h5-baseband.call
# and /dev/dlci.h5-baseband.sms
Receiving a (fictional) SMS:
# Receiving an unsollicited text message (AT+CMT).
[recv] AT+CMT=10307919127163385F901000B914161387976F0000066C8721E640C8B592090F28D76838661793B3C5E83D
0657959079AD2D36C3628EDA697E5E539BD4C06A5DD203A3A3D07C1DFF3343DFD76837E202ABA0E92C1E86850339C0
7C96031180846D3C9642ED80C046F8350C72675154B01
Text message are PDU encoded, you can find plenty of online decoder. Here is the plain text version SMSC: +19726133589
Receiver: +1416839XXXX
Payload: Hey Fab, John Carmack here: Still interested in this position ? Thu 20th May 2010 04:22.03PM
Receiving a call :
[recv] RING # Trigger the phone to ring
[recv] +CLIP: "",128,,,,2 # No caller ID :/ !
[recv] +XCALLSTAT: 1,4
[recv] RING[recv] +CLIP: "",128,,,,2
[send] ata # Local user decided to accept the incoming call
[recv] +XCALLSTAT: 1,0 # Reporting call status is enabled (1), voice is active (0)
[recv] OK
[recv] +XCALLSTAT: 1,6 # Reporting call status is enabled (1), voice is disconnected (6)
[send] at+ceer # Local user hang up
[recv] NO CARRIER # Connection is indeed terminated from the other hand
[recv] +CEER: "Release","Normal call clearing" #Collect informations on call
[recv] OK
Note : I was surprised to find RING command notifications note only in the call channel but also in the sms channel but it actually makes a lot of sense when connect to EDGE/GPRS: Since text message and call are not supported simultaneously the sms pseudo-terminal must remain silent during a call.now i will put up part2 where i will give the code capable of MITM
1 comment:
i was looking for a proper guideline on the procedure because even a single wrong step could make it all go to waste. thank you for posting with this it helps a lot
Post a Comment