The following is a snippet of original C++ code:
static __declspec(noinline) int ProcessData(const int* data, size_t size, int operationType) {
int result = 0;
if (operationType == 1) {
// Operation 1: Sum
for (size_t i = 0; i < size; ++i) {
result += data[i];
}
}
else if (operationType == 2) {
// Operation 2: Product
result = 1;
for (size_t i = 0; i < size; ++i) {
result *= data[i];
}
}
else {
// Operation 3: Count even numbers
for (size_t i = 0; i < size; ++i) {
if (data[i] % 2 == 0) result++;
}
}
return result;
}
Below is the decompiled output from IDA:
int __cdecl ProcessData(const int *myData, unsigned int size, int operationType)
{
unsigned int k; // [esp+D0h] [ebp-2Ch]
unsigned int j; // [esp+DCh] [ebp-20h]
unsigned int i; // [esp+E8h] [ebp-14h]
int result; // [esp+F4h] [ebp-8h]
__CheckForDebuggerJustMyCode(&E2398E0F_ConsoleApplication1_cpp);
result = 0;
if ( operationType == 1 )
{
for ( i = 0; i < size; ++i )
result += myData[i];
}
else if ( operationType == 2 )
{
result = 1;
for ( j = 0; j < size; ++j )
result *= myData[j];
}
else
{
for ( k = 0; k < size; ++k )
{
if ( !(myData[k] % 2) )
++result;
}
}
return result;
}
There is an issue with IDAās output here: it converts the original three block-scoped variables, which all shared the name i, into three separate function-scoped variables i, j, and k. How can we make IDAās output format support the original C++ style of block-scoped variables? If itās not currently supported, could this feature be added in the future?
Binary Ninja is already close to supporting this. Here is its output:
00412330 int32_t ProcessData(int32_t const* data, uint32_t size, int32_t operationType)
00412330 {
00412330 void var_34;
00412349 __builtin_memset(&var_34, 0xcccccccc, 0x30);
00412350 uint8_t* entry_JMC_flag;
00412350 j_@__CheckForDebuggerJustMyCode@4(entry_JMC_flag);
00412356 int32_t result = 0;
00412356
00412361 if (operationType == 1)
00412361 {
00412363 for (int32_t i = 0; i < size; i += 1)
0041237b result += data[i];
00412361 }
00412361 else if (operationType != 2)
00412394 {
004123cb for (int32_t i_1 = 0; i_1 < size; i_1 += 1)
004123e3 {
004123e3 int32_t edx_6 = data[i_1] & 0x80000001;
004123e3
004123f4 if (edx_6 < 0)
004123fa edx_6 = ((edx_6 - 1) | 0xfffffffe) + 1;
004123fa
004123fd if (!edx_6)
00412405 result += 1;
004123e3 }
00412394 }
00412394 else
00412394 {
00412396 result = 1;
00412396
004123b5 for (int32_t i_2 = 0; i_2 < size; i_2 += 1)
004123b5 result *= data[i_2];
00412394 }
00412394
00412418 j___RTC_CheckEsp();
00412420 return result;
00412330 }
In practical reverse engineering, block-scoped variables are highly necessary. For example, in functions compiled with optimizations, a single memory location (like a specific register or stack slot) might be reused across multiple scopes.
For instance, its role in the 1st scope might be isValue, in the 2nd scope it might be index, and in the 3rd scope it might be age. In IDAās current C89-style pseudocode, we are forced to name this single reused variable something cumbersome like isValue__OR__index__OR__age. Alternatively, we could create a union as an indirect workaround, but unions require manually selecting the correct member for every single code block, which is very tedious.
When functions are inlined, this issue becomes magnified and severely disrupts analysis. Therefore, I sincerely hope the official development team can add support for block-scoped variables in the decompiler.