0

This is my first question on this forum, so I hope that it would not be offensive or redundant.

So recently I am attempting to developing some kernel extensions for Apple's macOS using the IOKit framework. However, in the debugging process, I encountered some confusions while looking at the assembly and pseudocode of my binaries generated by IDA.

Currently, the greatest issue is how the normal switch case statement is interrupted by the decompiler — _bittest64 commands would be yielded with huge numbers as arguments. I am somewhat sure that it has to do with the “lower-bound” of the cases, but I don't know how exactly should I comprehend it. Here is an example:

Actual code Hex-Rays decompiler pseudocde

I can't really see how they relate, particularly the _bittest64 part. Just by the way, the enum kBluetoothIntelHardwareVariantJfP = 0x11 and kBluetoothIntelHardwareVariantSlrF = 0x19.

Happy thanksgiving!

1
  • 1
    Welcome to Reverse Engineering StackExchange! Please copy / paste your code as a text instead of pictures. It will help others to help you.
    – MarianD
    Nov 27, 2021 at 19:48

1 Answer 1

1

It's a compiler optimization used to implement switch statements that have many cases leading to one location. Note that the weird output is not Hex-Rays' doing, but rather, a more-or-less direct translation of what's in the assembly language. You didn't show the assembly for your snippet, but here's similar disassembly from a database I have open:

.text:0000000061FEA1C8     lea     eax, [rdi-33h]
.text:0000000061FEA1CB     cmp     eax, 0Eh
.text:0000000061FEA1CE     ja      short loc_61FEA1F8
.text:0000000061FEA1CE
.text:0000000061FEA1D0     mov     edx, 6381h
.text:0000000061FEA1D5     bt      edx, eax ; <- this becomes _bittest
.text:0000000061FEA1D8     jnb     short loc_61FEA1F8

And here's its Hex-Rays decompilation:

  if ( (vChildOp - 51) > 0xE )
    return /* ... */;

  v6 = 25473;
  if ( !_bittest(&v6, vChildOp - 51) )
    return /* ... */;

Let's take a closer look at your snippet. You said that kBluetoothIntelHardwareVariantJfP = 0x11 and kBluetoothIntelHardwareVariantSlrF = 0x19. Now look at the constant, 0x39E0000LL. This is 11100111100000000000000000b in binary:

11 1001 1110 0000 0000 0000 0000
|| |  | |||- bits 0-16 clear   
|| |  | ||-- bit 17 (i.e., 0x11): first bit set
|| |  | |--- bit 0x12 set
|| |  | ---- bit 0x13 set
|| |  ------ bit 0x14 set
|| --------- bit 0x17 set
|----------- bit 0x18 set
------------ bit 0x19: last bit set

Notice that the switch statement in your example has 7 cases leading to the same label, and that two of the cases correspond to bits 0x19 and 0x11? It is not a coincidence that the constant in the snippet has 7 bits set, including bits 0x11 and 0x19! The bt instruction, translated as the _bittest intrinsic, is just checking to see whether hardwareVariant, or a2 in the decompilation, is one of the values 0x11-0x14 or 0x17-0x19. It performs all 7 comparisons at the same time.

The if-statement on the outside is the default check: it ensures that the value of a2 is at most 0x19 -- if it's not, it jumps to the default location, i.e., does nothing.

5
  • OT on a simple glance result is an unassigned local a garbage only assigned a value inside the if block{} so does this function return garbage?
    – blabb
    Nov 29, 2021 at 16:01
  • @blabb Note that, in the source code, the function's true return type is void. Hex-Rays erroneously thinks this function returns a value, so it tries to create an assignment expression for the rax register. In this case, the only such assignment takes place as part of the bt sequence, which is why we get garbage inside of an if-statement. Nov 29, 2021 at 16:06
  • Thanks and again OT so the Function Type also needs to be modified manually as void blah(x,y) instead of __int64 to fix this but if there was no source to rely how to grapple with this apparently erroneous return in general
    – blabb
    Nov 29, 2021 at 17:27
  • @blabb I rely on clues like the one you identified. If some paths through a function don't return a value, it probably returns void. If none of the calling functions use the return value, it may or may not be void, but there's no harm in marking it as such. If a function supposedly returns void, but calling functions show usage of uninitialized values allocated in the rax register after the call, it does not return void. There's a recursive effect; if you determine a called function returns void, that may give you evidence that a calling function does also. Nov 29, 2021 at 18:08
  • ok thanks again actually i was looking for some magic recipes :) to peer at decompiled output without accompanying disassembly or source
    – blabb
    Nov 29, 2021 at 18:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.