# mypy: ignore-errors
#!/usr/bin/env python3
import os , ctypes , ctypes . util , struct , platform , time
from tinygrad . runtime . autogen import libc , qcom_dsp
def to_mv ( ptr , sz ) - > memoryview : return memoryview ( ctypes . cast ( ptr , ctypes . POINTER ( ctypes . c_uint8 * sz ) ) . contents ) . cast ( " B " )
from hexdump import hexdump
def get_struct ( argp , stype ) :
return ctypes . cast ( ctypes . c_void_p ( argp ) , ctypes . POINTER ( stype ) ) . contents
def format_struct ( s ) :
sdats = [ ]
for field in s . _fields_ :
dat = getattr ( s , field [ 0 ] )
if isinstance ( dat , int ) : sdats . append ( f " { field [ 0 ] } :0x { dat : X } " )
elif hasattr ( dat , " _fields_ " ) : sdats . append ( ( field [ 0 ] , format_struct ( dat ) ) )
elif field [ 0 ] == " PADDING_0 " : pass
else : sdats . append ( f " { field [ 0 ] } : { dat } " )
return sdats
@ctypes . CFUNCTYPE ( ctypes . c_int , ctypes . c_int , ctypes . c_ulong , ctypes . c_void_p )
def ioctl ( fd , request , argp ) :
fn = os . readlink ( f " /proc/self/fd/ { fd } " )
idir , size , itype , nr = ( request >> 30 ) , ( request >> 16 ) & 0x3FFF , ( request >> 8 ) & 0xFF , request & 0xFF
if fn == " /dev/adsprpc-smd " :
if nr == 1 :
st = get_struct ( argp , qcom_dsp . struct_fastrpc_ioctl_invoke )
method = ( st . sc >> 24 ) & 0xFF
in_args = ( st . sc >> 16 ) & 0xFF
out_args = ( st . sc >> 8 ) & 0xFF
if out_args :
for arg in range ( in_args , in_args + out_args ) :
ctypes . memset ( st . pra [ arg ] . buf . pv , 0 , st . pra [ arg ] . buf . len )
# print("enter", libc.gettid())
ret = libc . syscall ( 0x1d , ctypes . c_int ( fd ) , ctypes . c_ulong ( request ) , ctypes . c_void_p ( argp ) )
# print("done", libc.gettid())
if fn == " /dev/ion " :
if nr == 0 :
st = get_struct ( argp , qcom_dsp . struct_ion_allocation_data )
print ( ret , " ION_IOC_ALLOC " , format_struct ( st ) )
elif nr == 1 :
st = get_struct ( argp , qcom_dsp . struct_ion_handle_data )
print ( ret , " ION_IOC_FREE " , format_struct ( st ) )
elif nr == 2 :
st = get_struct ( argp , qcom_dsp . struct_ion_fd_data )
print ( ret , " ION_IOC_MAP " , format_struct ( st ) )
elif fn == " /dev/adsprpc-smd " :
assert chr ( itype ) == ' R '
if nr == 8 :
st = ctypes . c_uint32 . from_address ( argp )
print ( ret , " FASTRPC_IOCTL_GETINFO " , st . value )
elif nr == 2 :
st = get_struct ( argp , qcom_dsp . struct_fastrpc_ioctl_mmap )
print ( ret , " FASTRPC_IOCTL_MMAP " , format_struct ( st ) )
elif nr == 1 :
# https://research.checkpoint.com/2021/pwn2own-qualcomm-dsp/
st = get_struct ( argp , qcom_dsp . struct_fastrpc_ioctl_invoke )
print ( ret , " FASTRPC_IOCTL_INVOKE " , format_struct ( st ) )
# 0xFF000000 = Method index and attribute (the highest byte)
# 0x00FF0000 = Number of input arguments
# 0x0000FF00 = Number of output arguments
# 0x000000F0 = Number of input handles
# 0x0000000F = Number of output handles
method = ( st . sc >> 24 ) & 0xFF
in_args = ( st . sc >> 16 ) & 0xFF
out_args = ( st . sc >> 8 ) & 0xFF
in_h = ( st . sc >> 4 ) & 0xF
out_h = ( st . sc >> 0 ) & 0xF
print ( f " \t m: { method } ia: { in_args } oa: { out_args } ih: { in_h } oh: { out_h } " )
if in_args or out_args :
for arg in range ( in_args + out_args ) :
print ( arg , format_struct ( st . pra [ arg ] ) )
if st . pra [ arg ] . buf . pv is not None :
ww = to_mv ( st . pra [ arg ] . buf . pv , st . pra [ arg ] . buf . len )
hexdump ( to_mv ( st . pra [ arg ] . buf . pv , st . pra [ arg ] . buf . len ) [ : 0x40 ] )
elif nr == 6 :
print ( ret , " FASTRPC_IOCTL_INIT " , format_struct ( ini := get_struct ( argp , qcom_dsp . struct_fastrpc_ioctl_init ) ) )
print ( os . readlink ( f " /proc/self/fd/ { ini . filefd } " ) )
# print(bytearray(to_mv(ini.file, ini.filelen)))
elif nr == 7 :
print ( ret , " FASTRPC_IOCTL_INVOKE_ATTRS " , format_struct ( ini := get_struct ( argp , qcom_dsp . struct_fastrpc_ioctl_invoke_attrs ) ) )
elif nr == 12 : print ( ret , " FASTRPC_IOCTL_CONTROL " , format_struct ( get_struct ( argp , qcom_dsp . struct_fastrpc_ioctl_control ) ) )
else :
print ( f " { ret } UNPARSED { nr } " )
else :
print ( " ioctl " , f " { idir =} { size =} { itype =} { nr =} { fd =} { ret =} " , fn )
return ret
def install_hook ( c_function , python_function ) :
orig_func = ( ctypes . c_char * 4096 ) ( )
python_function_addr = ctypes . cast ( ctypes . byref ( python_function ) , ctypes . POINTER ( ctypes . c_ulong ) ) . contents . value
# AARCH64 trampoline to ioctl
# 0x0000000000000000: 70 00 00 10 adr x16, #0xc
# 0x0000000000000004: 10 02 40 F9 ldr x16, [x16]
# 0x0000000000000008: 00 02 1F D6 br x16
tramp = b " \x70 \x00 \x00 \x10 \x10 \x02 \x40 \xf9 \x00 \x02 \x1f \xd6 "
tramp + = struct . pack ( " Q " , python_function_addr )
# get real ioctl address
ioctl_address = ctypes . cast ( ctypes . byref ( c_function ) , ctypes . POINTER ( ctypes . c_ulong ) )
# hook ioctl
ret = libc . mprotect ( ctypes . c_ulong ( ( ioctl_address . contents . value / / 0x1000 ) * 0x1000 ) , 0x2000 , 7 )
assert ret == 0
ret = libc . mprotect ( ctypes . c_ulong ( ( ctypes . addressof ( orig_func ) / / 0x1000 ) * 0x1000 ) , 0x3000 , 7 )
assert ret == 0
libc . memcpy ( orig_func , ioctl_address . contents , 0x1000 )
libc . memcpy ( ioctl_address . contents , ctypes . create_string_buffer ( tramp ) , len ( tramp ) )
return orig_func
libc = ctypes . CDLL ( ctypes . util . find_library ( " libc " ) )
#install_hook(libc.ioctl, ioctl)
adsp = ctypes . CDLL ( ctypes . util . find_library ( " adsprpc " ) )
def send_rpc_invoke ( filename ) :
pass
if __name__ == " __main__ " :
print ( " calculator_open " )
# /dsp/cdsp/fastrpc_shell_3
handle = ctypes . c_int64 ( - 1 )
z = adsp . remote_handle64_open ( ctypes . create_string_buffer ( b " file:///libcalculator_skel.so?calculator_skel_handle_invoke&_modver=1.0&_dom=cdsp " ) ,
ctypes . byref ( handle ) )
print ( " handle " , z , hex ( handle . value ) )
assert handle . value != - 1
test = ( ctypes . c_int32 * 100 ) ( )
for i in range ( 100 ) : test [ i ] = i
print ( " calculator_sum " )
pra = ( qcom_dsp . union_remote_arg64 * 3 ) ( )
#arg_0 = ctypes.c_int32(100)
arg_0 = ctypes . c_int32 ( 100 )
arg_2 = ctypes . c_int64 ( - 1 )
pra [ 0 ] . buf . pv = ctypes . addressof ( arg_0 )
pra [ 0 ] . buf . len = 4
pra [ 1 ] . buf . pv = ctypes . addressof ( test )
pra [ 1 ] . buf . len = 0x190
pra [ 2 ] . buf . pv = ctypes . addressof ( arg_2 )
pra [ 2 ] . buf . len = 8
adsp . remote_handle64_invoke ( handle , ( 2 << 24 ) | ( 2 << 16 ) | ( 1 << 8 ) , pra )
print ( arg_2 . value )
print ( " done " )
print ( " closing " )
x = adsp . remote_handle64_close ( handle )
print ( x )
print ( " dun " )
os . _exit ( 0 )