Wrong argument passed to function in HLIL vs disassembly
Version and Platform (required):
- Binary Ninja Version: 4.1.5474-dev, f6ba0af7
- OS: macos
- OS Version: 14.5
- CPU Architecture: arm64
Internal binary major dine favor.
In DjiStreamChannelUsbBulk_AddPort...
For the call to UsbBulkInit IDA shows
v17 = (usb->UsbBulkInit)(v15, v16, &s_channelUsbBulkPortContent + 144 * i + 2);
whereas BN shows
000a434c int64_t usbBulkInfo
000a434c uint64_t err_1 = usb->UsbBulkInit(usbBulkInfo, usbBulkHandle: nullptr)
The first argument is a 12-byte structure that's passed on the stack
struct T_DjiHalUsbBulkInfo __packed
{
bool isUsbHost;
short unsigned int pid;
short unsigned int vid;
struct __packed
{
short unsigned int interfaceNum;
short unsigned int endPointIn;
short unsigned int endPointOut;
} channelInfo;
};
Looking at the IDA stack layout (clicking on v15 points at var_40) shows that there's indeed 12 bytes of space available (var_40 + var_38.
-0000000000000044 var_44 DCW ?
-0000000000000042 DCB ? ; undefined
-0000000000000041 var_41 DCB ?
-0000000000000040 var_40 DCQ ?
-0000000000000038 var_38 DCD ?
-0000000000000034 DCB ? ; undefined
-0000000000000033 DCB ? ; undefined
-0000000000000032 DCB ? ; undefined
-0000000000000031 DCB ? ; undefined
-0000000000000030 var_30 DCQ ?
-0000000000000028 var_28 DCQ ?
-0000000000000020 osal DCQ ?
-0000000000000018 usb DCQ ?
-0000000000000010 DCB ? ; undefined
-000000000000000F DCB ? ; undefined
-000000000000000E DCB ? ; undefined
-000000000000000D DCB ? ; undefined
-000000000000000C var_C DCD ?
-0000000000000008 var_8 DCD ?
-0000000000000004 var_4 DCD ?
+0000000000000000
+0000000000000000 ; end of stack variables
I don't know why IDA insists on passing 3 arguments to UsbBulkInit, I asked their support and will update this ticket when I have the answer. Still, BN insists that the function takes usbBulkInfo as its first argument, or 8 bytes passed in register x29.
I tried typing var_40 as T_DjiHalUsbBulkInfo, per the signature of UsbBulkInit. This changed BN output like so but didn't change the argument passed to UsbBulkInit.
000a42ec bulk_info.isUsbHost = arg1
000a42f4 bulk_info.pid = arg2
000a42fc bulk_info.vid = arg3
000a4304 bulk_info.channelInfo.interfaceNum = arg4
000a430c bulk_info.channelInfo.endPointIn = arg5
000a4314 bulk_info.channelInfo.endPointOut = arg6
000a434c int64_t usbBulkInfo
000a434c uint64_t err_1 = usb->UsbBulkInit(usbBulkInfo, usbBulkHandle: nullptr)
Dropping down to MLIL showed register x29 as argument
73 @ 000a434c err_1 = x3_2(usbBulkInfo: __saved_x29, usbBulkHandle: nullptr)
Corresponding disassembly shows register x29 being used to set things up but x0 points to bulk_info, as it should.
000a4344 a01b40f9 ldr x0, [x29, #0x30 {bulk_info.isUsbHost.q}] {0x0}
000a4348 a13b40b9 ldr w1, [x29, #0x38 {bulk_info.channelInfo.endPointIn.d}] {0x0}
000a434c 60003fd6 blr x3
Does this mean that HLIL is wrong?
Setup
000a42e8 a0bf4039 ldrb w0, [x29, #0x2f {var_41}]
000a42ec a0c30039 strb w0, [x29, #0x30 {bulk_info.isUsbHost}]
000a42f0 a05b4079 ldrh w0, [x29, #0x2c {var_44}]
000a42f4 a0670079 strh w0, [x29, #0x32 {bulk_info.pid}]
000a42f8 a0574079 ldrh w0, [x29, #0x2a {var_46}]
000a42fc a06b0079 strh w0, [x29, #0x34 {bulk_info.vid}]
000a4300 a0534079 ldrh w0, [x29, #0x28 {var_48}]
000a4304 a06f0079 strh w0, [x29, #0x36 {bulk_info.channelInfo.interfaceNum}]
000a4308 a04f4079 ldrh w0, [x29, #0x26 {var_4a}]
000a430c a0730079 strh w0, [x29, #0x38 {bulk_info.channelInfo.endPointIn}]
000a4310 a04b4079 ldrh w0, [x29, #0x24 {var_4c}]
000a4314 a0770079 strh w0, [x29, #0x3a {bulk_info.channelInfo.endPointOut}]
000a4318 a02f40f9 ldr x0, [x29, #0x58 {var_18}]
000a431c 030040f9 ldr x3, [x0 {T_DjiHalUsbBulkHandler::UsbBulkInit}]
000a4320 a16f80b9 ldrsw x1, [x29, #0x6c {i}]
000a4324 e00301aa mov x0, x1
000a4328 00f07dd3 lsl x0, x0, #0x3
000a432c 0000018b add x0, x0, x1
000a4330 00ec7cd3 lsl x0, x0, #0x4
000a4334 610f00d0 adrp x1, 0x292000
000a4338 21e03391 add x1, x1, #0xcf8 {s_channelUsbBulkPortContent}
000a433c 0000018b add x0, x0, x1
000a4340 02080091 add x2, x0, #0x2
000a4344 a01b40f9 ldr x0, [x29, #0x30 {bulk_info.isUsbHost.q}] {0x0}
000a4348 a13b40b9 ldr w1, [x29, #0x38 {bulk_info.channelInfo.endPointIn.d}] {0x0}
000a434c 60003fd6 blr x3
IDA listing
__int64 __fastcall DjiStreamChannelUsbBulk_AddPort(
char a1,
__int16 a2,
__int16 a3,
unsigned __int16 a4,
__int16 a5,
__int16 a6,
_QWORD *a7)
{
__int64 v15; // [xsp+30h] [xbp+30h]
unsigned int v16; // [xsp+38h] [xbp+38h]
__int64 v17; // [xsp+40h] [xbp+40h]
__int64 v18; // [xsp+40h] [xbp+40h]
_BYTE *v19; // [xsp+48h] [xbp+48h]
T_DjiOsalHandler *osal; // [xsp+50h] [xbp+50h]
T_DjiHalUsbBulkHandler *usb; // [xsp+58h] [xbp+58h]
int k; // [xsp+64h] [xbp+64h]
int j; // [xsp+68h] [xbp+68h]
int i; // [xsp+6Ch] [xbp+6Ch]
usb = DjiPlatform_GetHalUsbBulkHandler();
osal = DjiPlatform_GetOsalHandler();
BYTE1(v15) = 0;
v19 = osal->Malloc(1LL);
if ( v19 )
{
for ( i = 0; i <= 4 && *(&s_channelUsbBulkPortContent + 144 * i) == 1; ++i )
;
if ( i > 0 )
{
for ( j = 0; j < i; ++j )
{
if ( a2 == *(&s_channelUsbBulkPortContent + 72 * j + 5)
&& a3 == *(&s_channelUsbBulkPortContent + 72 * j + 6)
&& a4 == *(&s_channelUsbBulkPortContent + 72 * j + 7)
&& a5 == *(&s_channelUsbBulkPortContent + 72 * j + 8)
&& a6 == *(&s_channelUsbBulkPortContent + 72 * j + 9) )
{
*a7 = *(&s_channelUsbBulkPortContent + 144 * j + 2);
++*(&s_channelUsbBulkPortContent + 144 * j + 1);
DjiLogger_Output(
"channel",
3,
"[%s:%d) Usb bulk port has inited, interfaceNum:%d.",
"DjiStreamChannelUsbBulk_AddPort",
457LL,
a4);
return 0LL;
}
}
}
LOBYTE(v15) = a1;
WORD1(v15) = a2;
WORD2(v15) = a3;
HIWORD(v15) = a4;
LOWORD(v16) = a5;
HIWORD(v16) = a6;
v17 = (usb->UsbBulkInit)(v15, v16, &s_channelUsbBulkPortContent + 144 * i + 2);
if ( v17 )
{
DjiLogger_Output(
"channel",
0,
"[%s:%d) Init usb bulk channel failed, ret:%08X.",
"DjiStreamChannelUsbBulk_AddPort",
473LL,
v17);
return v17;
}
else
{
*(&s_channelUsbBulkPortContent + 72 * i + 5) = a2;
*(&s_channelUsbBulkPortContent + 72 * i + 6) = a3;
*(&s_channelUsbBulkPortContent + 72 * i + 7) = a4;
*(&s_channelUsbBulkPortContent + 72 * i + 8) = a5;
*(&s_channelUsbBulkPortContent + 72 * i + 9) = a6;
for ( k = 0; k <= 9; ++k )
{
*(&s_channelUsbBulkPortContent + 72 * i + 5 * k + 22) = 0;
*(&s_channelUsbBulkPortContent + 144 * i + 10 * k + 46) = 0LL;
}
*(&s_channelUsbBulkPortContent + 36 * i + 7) = 0;
*(&s_channelUsbBulkPortContent + 144 * i + 20) = osal->Malloc(0x80000LL);
if ( *(&s_channelUsbBulkPortContent + 144 * i + 20) )
{
*v19 = i;
v18 = osal->TaskCreate(
"bulk_recv_task",
DjiStreamChannelUsbBulk_RecvDataThread,
2048LL,
v19,
(&s_channelUsbBulkPortContent + 144 * i + 36));
if ( v18 )
{
DjiLogger_Output(
"channel",
0,
"[%s:%d) Create usb bulk recv task failed, ret:%08X.",
"DjiStreamChannelUsbBulk_AddPort",
501LL,
v18);
return v18;
}
else
{
*(&s_channelUsbBulkPortContent + 144 * i) = 1;
++*(&s_channelUsbBulkPortContent + 144 * i + 1);
*a7 = *(&s_channelUsbBulkPortContent + 144 * i + 2);
return 0LL;
}
}
else
{
DjiLogger_Output(
"channel",
0,
"[%s:%d) Malloc failed, ret:%08X.",
"DjiStreamChannelUsbBulk_AddPort",
491LL,
0LL);
return 226LL;
}
}
}
else
{
DjiLogger_Output("channel", 0, "[%s:%d) Malloc error", "DjiStreamChannelUsbBulk_AddPort", 438LL);
return 226LL;
}
}
BN HLIL
000a4000 uint64_t DjiStreamChannelUsbBulk_AddPort(char arg1, int16_t arg2, int16_t arg3, int16_t arg4, int16_t arg5, int16_t arg6, int64_t* arg7)
000a4024 struct T_DjiHalUsbBulkHandler* usb = DjiPlatform_GetHalUsbBulkHandler()
000a402c struct T_DjiOsalHandler* osal = DjiPlatform_GetOsalHandler()
000a4034 int64_t var_28 = 0
000a4038 struct T_DjiHalUsbBulkInfo bulk_info
000a4038 bulk_info.isUsbHost = 0
000a4038 bulk_info.pid = 0
000a4038 bulk_info.vid = 0
000a4038 bulk_info.channelInfo.interfaceNum = 0
000a403c bulk_info.channelInfo.endPointIn = 0
000a403c bulk_info.channelInfo.endPointOut = 0
000a404c void* arg = osal->Malloc(size: 1)
000a405c uint64_t err
000a4000
000a405c if (arg == 0)
000a4088 DjiLogger_Output(tag: "channel", level: 0, fmt: "[%s:%d) Malloc error", "DjiStreamChannelUsbBulk_AddPort", 0x1b6)
000a408c err = 0xe2
000a405c else
000a40e4 int32_t i
000a4094
000a40e4 for (i = 0; i s<= 4; i += 1)
000a40cc if (zx.d(*(&s_channelUsbBulkPortContent + sx.q(i) * 0x90) ^ 1) != 0)
000a40cc break
000a4094
000a40f8 int32_t i_1
000a4094
000a40f8 if (i s> 0)
000a42e4 for (i_1 = 0; i_1 s< i; i_1 += 1)
000a4134 if (zx.d(arg2) == zx.d(*(sx.q(i_1) * 0x90 + &data_292d02)) && zx.d(arg3) == zx.d(*(sx.q(i_1) * 0x90 + 0x292d04)) && zx.d(arg4) == zx.d(*(sx.q(i_1) * 0x90 + 0x292d06)) && zx.d(arg5) == zx.d(*(sx.q(i_1) * 0x90 + 0x292d08)) && zx.d(arg6) == zx.d(*(sx.q(i_1) * 0x90 + 0x292d0a)))
000a4204 break
000a40fc
000a42e4 if (i_1 s< i)
000a4230 *arg7 = *(sx.q(i_1) * 0x90 + 0x292cfa)
000a428c *(sx.q(i_1) * 0x90 + 0x292cf9) += 1
000a42c0 DjiLogger_Output(tag: "channel", level: 3, fmt: "[%s:%d) Usb bulk port has inited…", "DjiStreamChannelUsbBulk_AddPort", 0x1c9, zx.q(arg4))
000a42c4 err = 0
000a4094
000a42e4 if (i s<= 0 || i_1 s>= i)
000a42ec bulk_info.isUsbHost = arg1
000a42f4 bulk_info.pid = arg2
000a42fc bulk_info.vid = arg3
000a4304 bulk_info.channelInfo.interfaceNum = arg4
000a430c bulk_info.channelInfo.endPointIn = arg5
000a4314 bulk_info.channelInfo.endPointOut = arg6
000a434c int64_t usbBulkInfo
000a434c uint64_t err_1 = usb->UsbBulkInit(usbBulkInfo, usbBulkHandle: nullptr)
000a42ec
000a435c if (err_1 != 0)
000a438c DjiLogger_Output(tag: "channel", level: 0, fmt: "[%s:%d) Init usb bulk channel fa…", "DjiStreamChannelUsbBulk_AddPort", 0x1d9, err_1)
000a4390 err = err_1
000a435c else
000a43c0 *(sx.q(i) * 0x90 + &data_292d02) = arg2
000a43ec *(sx.q(i) * 0x90 + 0x292d04) = arg3
000a4418 *(sx.q(i) * 0x90 + 0x292d06) = arg4
000a4444 *(sx.q(i) * 0x90 + 0x292d08) = arg5
000a4470 *(sx.q(i) * 0x90 + 0x292d0a) = arg6
000a43a0
000a4518 for (int32_t i_2 = 0; i_2 s<= 9; i_2 += 1)
000a44bc *(sx.q(i_2) * 0xa + sx.q(i) * 0x90 + 0x292d24) = 0
000a4500 *(sx.q(i_2) * 0xa + sx.q(i) * 0x90 + 0x292d26) = 0
000a43a0
000a4540 *(sx.q(i) * 0x90 + 0x292d14) = 0
000a457c *(sx.q(i) * 0x90 + 0x292d0c) = osal->Malloc(size: 0x80000)
000a43a0
000a45ac if (*(sx.q(i) * 0x90 + 0x292d0c) == 0)
000a45dc DjiLogger_Output(tag: "channel", level: 0, fmt: "[%s:%d) Malloc failed, ret:%08X.", "DjiStreamChannelUsbBulk_AddPort", 0x1eb, err_1)
000a45e0 err = 0xe2
000a45ac else
000a45f4 *arg = i.b
000a4644 uint64_t err_2 = osal->TaskCreate(name: "bulk_recv_task", taskFunc: DjiStreamChannelUsbBulk_RecvDataThread, stackSize: 0x800, arg, task: sx.q(i) * 0x90 + 0x292d1c)
000a45f4
000a4654 if (err_2 == 0)
000a46b4 *(&s_channelUsbBulkPortContent + sx.q(i) * 0x90) = 1
000a4710 *(sx.q(i) * 0x90 + 0x292cf9) += 1
000a473c *arg7 = *(sx.q(i) * 0x90 + 0x292cfa)
000a4740 err = 0
000a4654 else
000a4684 DjiLogger_Output(tag: "channel", level: 0, fmt: "[%s:%d) Create usb bulk recv tas…", "DjiStreamChannelUsbBulk_AddPort", 0x1f5, err_2)
000a4688 err = err_2
000a4000
000a4748 return err
This is what BN thinks the signature of HalUsbBulk_Init is
long unsigned int HalUsbBulk_Init(struct usbBulkInfo { bool isUsbHost; __padding char _1[0x1];short unsigned int pid; short unsigned int vid; struct channelInfo; }, void** usbBulkHandle)
BN HLIL looks wrong compared to IDA. Note the missing argument name for usbBulkInfo.
000462f8 long unsigned int HalUsbBulk_Init(struct usbBulkInfo, void** usbBulkHandle)
00046308 void** usbBulkHandle_1 = usbBulkHandle
00046314 int32_t x1
00046314 int32_t var_18 = 0 | (x1 & 0xffffffff)
00046318 int64_t var_8 = 0
0004632c int64_t* x2
0004632c *x2 = malloc(n: 0x20)
0004633c int64_t result
000462f8
0004633c if (*x2 == 0)
00046340 result = 0xec
0004633c else if (zx.d(usbBulkHandle_1.b) == 0)
00046418 **x2 = var_8
00046430 memcpy(*x2 + 0x14, &usbBulkHandle_1, 0xc)
00046440 *(*x2 + 0x10) = zx.d(usbBulkHandle_1:6.w)
00046418
0004644c if (zx.d(usbBulkHandle_1:6.w) == 7)
00046468 *(*x2 + 8) = open(file: "/dev/usb-ffs/bulk1/ep1", oflag: 2)
00046468
0004647c if (*(*x2 + 8) s< 0)
00046480 result = 0xec
0004647c else
000464a0 *(*x2 + 0xc) = open(file: "/dev/usb-ffs/bulk1/ep2", oflag: 2)
000464a0
000464b4 if (*(*x2 + 0xc) s>= 0)
000464b4 goto label_4653c
000464a0
000464b8 result = 0xec
0004644c else
000464c8 if (zx.d(usbBulkHandle_1:6.w) != 8)
000464c8 goto label_4653c
000464c8
000464e4 *(*x2 + 8) = open(file: "/dev/usb-ffs/bulk2/ep1", oflag: 2)
000464c8
000464f8 if (*(*x2 + 8) s< 0)
000464fc result = 0xec
000464f8 else
0004651c *(*x2 + 0xc) = open(file: "/dev/usb-ffs/bulk2/ep2", oflag: 2)
0004651c
00046530 if (*(*x2 + 0xc) s>= 0)
00046530 goto label_4653c
0004651c
00046534 result = 0xec
00046350 else if (libusb_init() s< 0)
0004636c result = 0xec
00046368 else
00046388 int64_t x0_11 = libusb_open_device_with_vid_pid()
00046388
00046398 if (x0_11 == 0)
0004639c result = 0xec
00046398 else if (libusb_claim_interface() != 0)
000463cc printf(format: "libusb claim interface error")
000463d4 libusb_close()
000463d8 result = 0xec
000463c0 else
000463ec **x2 = x0_11
00046404 memcpy(*x2 + 0x14, &usbBulkHandle_1, 0xc)
0004653c label_4653c:
0004653c result = 0
000462f8
00046548 return result
IDA
T_DjiReturnCode __cdecl HalUsbBulk_Init(T_DjiHalUsbBulkInfo usbBulkInfo, T_DjiUsbBulkHandle *usbBulkHandle)
{
_DWORD *v3; // x19
_DWORD *v4; // x19
_DWORD *v5; // x19
_DWORD *v6; // x19
T_DjiHalUsbBulkInfo usbBulkInfoa; // [xsp+30h] [xbp+30h] BYREF
libusb_device_handle *handle; // [xsp+48h] [xbp+48h]
usbBulkInfoa = usbBulkInfo;
handle = 0LL;
*usbBulkHandle = malloc(0x20uLL);
if ( !*usbBulkHandle )
return 236LL;
if ( usbBulkInfoa.isUsbHost )
{
if ( libusb_init(0LL) < 0 )
return 236LL;
handle = libusb_open_device_with_vid_pid(0LL, usbBulkInfoa.vid, usbBulkInfoa.pid);
if ( !handle )
return 236LL;
if ( libusb_claim_interface(handle, usbBulkInfoa.channelInfo.interfaceNum) )
{
printf("libusb claim interface error");
libusb_close(handle);
return 236LL;
}
**usbBulkHandle = handle;
memcpy(*usbBulkHandle + 0x14, &usbBulkInfoa, 0xCuLL);
}
else
{
**usbBulkHandle = handle;
memcpy(*usbBulkHandle + 20, &usbBulkInfoa, 0xCuLL);
*(*usbBulkHandle + 4) = usbBulkInfoa.channelInfo.interfaceNum;
if ( usbBulkInfoa.channelInfo.interfaceNum == 7 )
{
v3 = *usbBulkHandle;
v3[2] = open("/dev/usb-ffs/bulk1/ep1", 2);
if ( *(*usbBulkHandle + 2) < 0 )
return 236LL;
v4 = *usbBulkHandle;
v4[3] = open("/dev/usb-ffs/bulk1/ep2", 2);
if ( *(*usbBulkHandle + 3) < 0 )
return 236LL;
}
else if ( usbBulkInfoa.channelInfo.interfaceNum == 8 )
{
v5 = *usbBulkHandle;
v5[2] = open("/dev/usb-ffs/bulk2/ep1", 2);
if ( *(*usbBulkHandle + 2) < 0 )
return 236LL;
v6 = *usbBulkHandle;
v6[3] = open("/dev/usb-ffs/bulk2/ep2", 2);
if ( *(*usbBulkHandle + 3) < 0 )
return 236LL;
}
}
return 0LL;
}
Fixing up the signature in BN shows the use of info but misses the connection between x2 and handle where the latter goes unassigned.
000462f8 uint64_t HalUsbBulk_Init(struct T_DjiHalUsbBulkInfo* info, T_DjiUsbBulkHandle* handle)
00046308 struct T_DjiHalUsbBulkInfo* info_1 = info
00046314 int32_t var_18 = 0 | (handle.d & 0xffffffff)
00046318 int64_t var_8 = 0
0004632c int64_t* x2
0004632c *x2 = malloc(n: 0x20)
0004633c int64_t result
000462f8
0004633c if (*x2 == 0)
00046340 result = 0xec
0004633c else if (zx.d(info_1.b) == 0)
00046418 **x2 = var_8
00046430 memcpy(*x2 + 0x14, &info_1, 0xc)
00046440 *(*x2 + 0x10) = zx.d(info_1:6.w)
00046418
0004644c if (zx.d(info_1:6.w) == 7)
00046468 *(*x2 + 8) = open(file: "/dev/usb-ffs/bulk1/ep1", oflag: 2)
00046468
0004647c if (*(*x2 + 8) s< 0)
00046480 result = 0xec
0004647c else
000464a0 *(*x2 + 0xc) = open(file: "/dev/usb-ffs/bulk1/ep2", oflag: 2)
000464a0
000464b4 if (*(*x2 + 0xc) s>= 0)
000464b4 goto label_4653c
000464a0
000464b8 result = 0xec
0004644c else
000464c8 if (zx.d(info_1:6.w) != 8)
000464c8 goto label_4653c
000464c8
000464e4 *(*x2 + 8) = open(file: "/dev/usb-ffs/bulk2/ep1", oflag: 2)
000464c8
000464f8 if (*(*x2 + 8) s< 0)
000464fc result = 0xec
000464f8 else
0004651c *(*x2 + 0xc) = open(file: "/dev/usb-ffs/bulk2/ep2", oflag: 2)
0004651c
00046530 if (*(*x2 + 0xc) s>= 0)
00046530 goto label_4653c
0004651c
00046534 result = 0xec
00046350 else if (libusb_init() s< 0)
0004636c result = 0xec
00046368 else
00046388 int64_t x0_11 = libusb_open_device_with_vid_pid()
00046388
00046398 if (x0_11 == 0)
0004639c result = 0xec
00046398 else if (libusb_claim_interface() != 0)
000463cc printf(format: "libusb claim interface error")
000463d4 libusb_close()
000463d8 result = 0xec
000463c0 else
000463ec **x2 = x0_11
00046404 memcpy(*x2 + 0x14, &info_1, 0xc)
0004653c label_4653c:
0004653c result = 0
000462f8
00046548 return result
IDA has the Force call type option that converts this
v17 = (usb->UsbBulkInit)(v15, v16, &s_channelUsbBulkPortContent + 144 * i + 2);
to this
v16 = usb->UsbBulkInit(v15, (&s_channelUsbBulkPortContent + 144 * i + 2));
I suspect this is a duplicate of #5992