views:

458

answers:

6

Hi,

I've just reverse engineered a binary with IDA and loaded Hex Ray to check out a specific function. The generate C source code contains the following if statement:

LP_ret_GlobalLock_1 = GlobalLock(hMem);
LP_ret_GlobalLock_2 = LP_ret_GlobalLock_1;
...
if ( !LP_ret_GlobalLock_1 || (v9 = *(_DWORD *)(v6 + 4), *(_DWORD *)v6 = LP_ret_GlobalLock_2, v9 < 0) )

I'm not sure to completely understand the following part:

(v9 = *(_DWORD *)(v6 + 4), *(_DWORD *)v6 = LP_ret_GlobalLock_2, v9 < 0)

v9 is initialised as v6 + 4; but then v6 is modified to be a pointer LP_ret_GlobalLock_2 and finally v9 is check for being less than 0. Is that correct? When calculating v9 what value is used for v6? The LP_ret_GlobalLock_2 or the previous value?

Thanks for that.

+2  A: 

The variable v6 is a pointer as far as I can see. Also, I assume that _DWORD is a 32 bit quantity. This is the breakdown:

  • v9 = *(_DWORD *)(v6 + 4): v9 is assigned to the 32 bit word following the 32 bit word pointed to by v6.
  • *(_DWORD *)v6 = LP_ret_GlobalLock_2: The 32 bit word pointed to by v6 is assigned the value of LP_ret_GlobalLock_2.
  • v9 < 0: The expression evaluates to v9 < 0, i.e. it is true if the 32 bit bit word following the 32 bit word pointed to by v6 is less than zero.

If _DWORD is an unsigned value v9 can never be negative and the expression is always false.

Martin Liversage
@Martin: it is decompiled code ...
Stephen C
Thanks for your answer. Are you guessing that v9 is an unsigned integer because of the _ in front of _DWORD? because from the assembly code is looks more like a signed integer.int v9; // eax@8
Benjamin
@Stephen C: Yes, I realized that and removed my statement about the qualtity of the code. @Benjamin: Having spent many years on the Windows platform I'm used to `DWORD` being a 32 bit unsigned value, but it has to be signed in your code if the `< 0` has to make any sense.
Martin Liversage
@Martin: ok thanks for that :)
Benjamin
+4  A: 

I guess you are asking about the comma operator. It evaluates the expression before the comma, then the expression after the comma, and the result of the whole thing is the result of the second expression.

So it first does v9 = *(_DWORD *)(v6 + 4), then *(_DWORD *)v6 = LP_ret_GlobalLock_2, and then v9 < 0. The result is the result of v9 < 0, after the first two expressions have been evaluated.

I understand that you got this via reverse engineering. I would never use the comma operator with side effects inside an if-statement like that when writing code myself; it's too obfuscated.

Jesper
+1  A: 

The comma operator in C is the sequence operator. So, your assumptions are right.

However, note that the second assignment does not change v6. It changes v6[0], the value that v6 points to. The assignment of v9 reads from v6[4].

MSalters
+1  A: 

If it is complied in C, then the expressions are evaluated from left to right. So when calculating v9, the initial value of v6 is used.

Eventually, v9 is checked against 0, so you're correct.

Simply put, the execution is equivalent to:

v9 = *(_DWORD *)(v6 + 4);
*(_DWORD *)v6 = LP_ret_GlobalLock_2;
return v9 < 0;

However, I would not recommend using the kind of intricated code you presented, which is hard to read and understand, and might be error prone when maintaining. Expanding the statements as standalone is much better.

Cătălin Pitiș
+2  A: 

The key to understanding what this statement does is the comma operator. The comma operator has lower precedence than all other operators. It evaluates all sub-expressions from left to right, then returns the value of the last one. So, for example,

int a, b;
b = (a=1, a+1);

would set b to 2.

So to answer your questions:

v9 is initialised as v6 + 4; but then v6 is modified to be a pointer LP_ret_GlobalLock_2 and finally v9 is check for being less than 0. Is that correct?

Yes.

When calculating v9 what value is used for v6? The LP_ret_GlobalLock_2 or the previous value?

The previous value.

Martin B
+1  A: 

The part

if (v9 = *(_DWORD *)(v6 + 4), *(_DWORD *)v6 = LP_ret_GlobalLock_2, v9 < 0)

is evaluated just like

v9 = *(_DWORD *)(v6 + 4);
*(_DWORD *)v6 = LP_ret_GlobalLock_2;
if (v9 < 0)
/* ... */

Any comma seperated expression is evaluated left to right, the result of the expression is the result of the right part. Relevant for your if-clause is (v9 < 0), with v9 as assigned in the first part before v6 is modified.

groovingandi