AppleH10CamIn::ISP_RegisterFirmwareWorkProcessor_gated(AppleH10CamIn *this, uint64_t *inStr, io_user_reference_t *asyncRef, uint64_t this2){
...
if ( some_os_array->getCount() ){
while ( true ){
unk_object = *(uint64_t*)(some_os_array->getObject());
idk = unk_object->unk_0xD8();
old_port = *(uint64_t*)(idk + 0x48);
port = *(uint64_t*)asyncRef; <----(a)
...
}
...
}
...
}
(a) By simply calling this externalMethod syncronously, asyncRef remains NULL thus NULL deref happens
This can be seen accross selector 43, 44, 45, 46. Selector 45 requires the camera to be turned on so you can run this PoC while taking a selfie.
if ( !*(&this->unk + 3) && this->ISP_powered_on )
LDR X11, [X11]
CMP X11, X9 <----(b)
B.LS #0x18
LDR X10, [X10]
LDR W9, [X10,X9] <----(a)
B #0x20
MOV W9, #0xbeef
MOVK W9, #0xdead, lsl #16
STR W9, [X8]
(a) X9 is user controlled (b) This is a bounds check with some member variable's value and I think it was 0x4000 on an iOS 13 iPhone 11 thus we can't use the 2 bytes 0x4141 leet number
There are some checks above (b) but like passing our leet number of choice 0x41 we can crash the kernel with an "Unaligned kernel data abort" message. This can be seen in selector 72 and 73 (AppleH10CamInUserClient::_ISP_SpmiRegRead() and AppleH10CamInUserClient::_ISP_SpmiRegWrite())
This function is parses a structure I named "session_str" twice. At first it takes in an 0x34 bytes buffer from the user and starts parsing it. The first 4 bytes is the number of "Groups" in the structure. Random thoughts: If we can spray OOL ports after the strIn message buffer we can probably make this code parse those messages and leak kernel pointers in the syslog but meh.
ISP_CreateMultiCameraSession_gated
if ( *strIn )
{
v8 = 0LL;
v9 = strIn + 0x10;
do
{
v10 = *(v9 - 2);
v11 = *v9;
v9 += 4;
if ( v11 == 1 )
v12 = "HardwareSync";
else
v12 = "NoSync";
_os_log_internal(
&_mh_execute_header,
&_os_log_default,
OS_LOG_TYPE_DEFAULT,
" Group=%d: ChannelMask=0x%08X SyncType=%s",
v8++,
v10,
v12);
}
while ( v8 < *strIn ); // no bounds check on *strIn
__int64 __fastcall AppleH10CamIn::ISP_RunAlgoChooseJasperPriPairIndices_gated(AppleH10CamIn *this, unsigned int camChan, mach_vm_address_t address, mach_vm_size_t length, task_t task)
{
if ( !length )
{
v18 = this->field_3c0 + 0x8A8LL * camChan; //field_3c0 is a 0x3400 sized buffer from what I see and it should not contain more than 6 objects
v14 = *(v18 + 0x1A0);
v15 = (v18 + 0x170);
projectionMode = this->field_CC;
inbuf = 0LL;
inbuflen = 0;
}
else{
v7 = length;
v8 = IOMemoryDescriptor::withAddressRange(address, length, 2u, task);
if ( !v8 )
_os_log_internal(
&_mh_execute_header,
&_os_log_default,
OS_LOG_TYPE_DEFAULT,
"AppleH10CamIn::%s - Error: could not create memory descriptor for update data\n",
"ISP_RunAlgoChooseJasperPriPairIndices_gated");
v9 = 0xE00002BDLL; // we don't actually return, which causes a NULL deref if we'd simply
if ( !(v8->preapre)(v8, 0LL) ) // fail the creation of the Memory Descriptor
{
v10 = (v8->map)(v8, 4096LL);
if ( v10 )
{
v11 = (v10->getVirtualAddress)();
if ( !v11 )
return 3758097090LL;
inbuf = v11;
v13 = this->field_3c0 + 0x8A8LL * camChan; <----- integer overflow
v14 = *(v13 + 0x1A0);
v15 = (v13 + 0x170);
projectionMode = this->field_CC;
inbuflen = v7;
goto LABEL_9;
}
}
}
v9 = JasperAgileClocking::JasperSACIndexSelect(v14, inbuf, inbuflen, v15, projectionMode);
_os_log_internal(
&_mh_execute_header,
&_os_log_default,
OS_LOG_TYPE_DEFAULT,
"AppleH10CamIn::%s - Called JasperSACIndexSelect with projection mode [%u]\n",
"ISP_RunAlgoChooseJasperPriPairIndices_gated",
this->field_CC);
if ( v9 )
{
_os_log_internal(
&_mh_execute_header,
&_os_log_default,
OS_LOG_TYPE_DEFAULT,
"AppleH10CamIn::%s - PRI selection returned 0x%X\n",
"ISP_RunAlgoChooseJasperPriPairIndices_gated",
v9);
}
else
{
v20 = 0LL;
v21 = 0x8A8LL * camChan + 0x174; <----- well well well another one...
do
{
_os_log_internal(
&_mh_execute_header,
&_os_log_default,
OS_LOG_TYPE_DEFAULT,
"AppleH10CamIn::%s - Selected parameter indices for bank [%d]= [%u,%u]\n",
"ISP_RunAlgoChooseJasperPriPairIndices_gated",
v20++,
*(this->field_3c0 + v21 - 4),
*(this->field_3c0 + v21));
v21 += 8LL;
}
while ( v20 != 4 );
v9 = 0LL;
}
return v9;
}
__int64 __fastcall JasperAgileClocking::CreateFList2PllArray(__int64 a1, unsigned int *inbuf, unsigned int inbuflen, _QWORD *a4, _DWORD *a5)
{
__int64 v5; // x8
__int64 v11; // x24
_DWORD *v12; // x0
__int64 v13; // x8
__int64 v14; // x11
__int64 v15; // x9
_DWORD *v16; // x11
__int64 v17; // x11
int v18; // w13
__int64 v19; // x15
unsigned int v20; // w16
int v21; // w17
int v22; // w16
__int64 v23; // x16
_DWORD *v24; // x17
v5 = 0xE00002C2LL;
if ( a4 && a5 )
{
v11 = inbuflen;
v12 = operator new[](20LL * inbuflen); <-- inbuflen is user controllable
if ( v12 )
{
*v12 = 0;
v13 = *(a1 + 0x440);
if ( inbuf )
v14 = *inbuf;
else
v14 = 0LL;
LODWORD(v15) = 0;
v16 = (v13 + 24 * v14); <--- v14 is user controllable
v12[2] = v16[5];
v12[4] = 0;
You can crash the kernel through this method in many ways iirc I had two different pocs for carshing at different locations. There are many integer overflows deep inside as well and the no return thing is pretty funny heh. Although field_3c0 is inited once at boot, the array size is very big and we can make our index pretty big too so I think if you can go very far in kernel memmory and if your stars are aligned you can probably corrupt some sprayed object but again idk if this is possible.
if ( *(user_buf__ + 0x34) )
{
v40 = 0LL;
do
{
v41 = *&v238[8 * v40];
if ( v41 )
{
if ( !v31 || (*(**(v15 + 0x1260) + 0x110LL))() )
{
LOBYTE(v32) = 1;
(*(*v41 + 0x90LL))(v41, 1LL);
(*(**(v15 + 0x228) + 0x90LL))();
v42 = *&v239[8 * v40];
(*(*v42 + 224LL))(v42, 2LL);
(*(*v42 + 40LL))(v42);
v43 = *&v240[8 * v40];
IOSurface::complete(v43);
IOSurface::deviceUnlockSurface(v43, 0);
(v43->release)(v43);
}
if ( (*(**(v15 + 4704) + 272LL))() )
(*(**(v15 + 0x1260) + 264LL))();
user_buf__ = user_buf_;
}
++v40;
}
while ( v40 < *(user_buf__ + 0x34) ); <-- user controllable
}
set custom index at 0x98 and 0x9C to be anything non zero in selector 42 and in same way 0x34 and 0x38 for selector 41 AppleH10CamIn::ISP_GeneralProcess_Generic_gated() and AppleH10CamIn::ISP_GeneralProcess_gated()
Another one lacking bounds check was ISP_PPMAdmissionCheck_gated() which was later on calling an assert so it's pretty much a meh thing but still. Are there more bugs tho? No definitely not - said my AI based sekure code auditor. Some notes on this driver: A11-A13 use this driver on A14 they use AppleH13Cam but from reversing it seemed like a copy paste of this one but with 1 extra external method iirc. These bugs have been made public bc Umaru Chan wanted that, so talk with her instead.