1

I have been tracing through my binary and converting it to C code so I can understand it easily. As I am going through the instructions and functions, I keep stumbling upon code that don't make sense. For example:

ROM:080004B8                 CMNNE.W         R4, #1
ROM:080004BC                 BEQ.W           loc_80007DE

This code will never branch. Another example:

STRB.W          R0, [SP,#0x18+var_18] @ where var_18 is -0x18

Which is similar to indexing an array in C like this "array[5-5]". Why is this happening? Is this the disassembler? A form of obfuscated code that is trying to frustrate me? A poor compiler?

  • Is this code between functions ? Is it ARM or Thumb ? – w s Jul 15 '16 at 5:46
  • 1
    It's been a while since I looked at ARM code, so I might be totally wrong - why are you saying your first example will never branch? if Z=1, CMNNE is not executed and BEQ branches; If Z=0, CMNNE is executed (testing R4) and BEQ would branch on the result. – Vitaly Osipov Jul 15 '16 at 8:52
  • The first example is between functions and the second example is inside a function. It is Thumb. @VitalyOsipov you are correct. I have edited the post by removing that example. – Tyler Jul 15 '16 at 12:11
  • Here is a better example. A value from the stack is loaded to R0, shifted, and saved in R0 (the value in the stack does not get updated). The next line loads a different value from the stack which removes the shifted value from the line before. What is the point of these instructions that have no purpose? – Tyler Jul 15 '16 at 13:04
  • 1
    IDA does this for you - intentionally. The reason is it keeps track of modifications of the stack pointer, and yet attempts to keep the (relative!) addresses of local variables the same, Quite helpful actually. – usr2564301 Jul 15 '16 at 13:08
3

To expand on Rad Lexus' comment, which describes the situation very well but may not be understandable without an example:

Within a function, the stack pointer, in most cases, isn't constant. It may change when parameters for a function/method/subroutine are pushed, when the programmer uses things like alloca, and for other reasons as well, depending on the compiler and processor.

This makes tracking the position of arguments and local variables quite hard. For example, code like f(i++) may result in something like

mov r0, [sp, #8]     ; read the variable
mov [sp], r0          ; put it on the argument stack
sub sp, sp, #4        ; adjust the stack pointer
add r0, r0, #1        ; calculate the ++
mov [sp, #12], r0    ; write back result

which is a version that makes the change to sp visible; normally, that change will be hidden in something like stmfd sp!, {r0}

In such cases, it's quite difficult to see that the "read" and "write" operations access the same address in memory while the sp value has changed.

This is why decompilers like IDA assign names to those locations - as an example, let's assume in my example the variable is named var_4 with a value of 4.

Now IDA can emit

mov r0, [sp, var_4+#4]     ; read the variable
mov [sp, var_4+#8], r0     ; write back result

with var_4 hinting at "the same variable", and the #4/#8 at the offset from [sp] that needs to be added to var_4 in each of these cases.

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.