使用具有不同优化的 gcc 时出现奇怪的反编译
Strange decompilation when using gcc with different optimization
我 运行正在 linux 5.4.18-1-MANJARO 上安装 gcc 版本 9.2.0 (GCC)
文件名:a.c
#include<stdio.h>
int
main(void) {
int a;
scanf("%d", &a);
if (a < 5 || a > 6)
puts("fail");
else
puts("succeed");
}
那我运行:
gcc a.c -O0 -o a.out
gcc a.c -O1 -o b.out
我用r2反编译了a.out,得到了这个
undefined8 main(void)
{
undefined8 uVar1;
int64_t in_FS_OFFSET;
int32_t var_ch;
int64_t canary;
canary = *(int64_t *)(in_FS_OFFSET + 0x28);
sym.imp.__isoc99_scanf(0x2004, &var_ch);
if ((var_ch < 5) || (6 < var_ch)) {
sym.imp.puts("fail");
} else {
sym.imp.puts("succeed");
}
uVar1 = 0;
if (canary != *(int64_t *)(in_FS_OFFSET + 0x28)) {
uVar1 = sym.imp.__stack_chk_fail();
}
return uVar1;
}
这是我所期望的。
但是当我反编译时b.out,我得到了这个
undefined8 main(void)
{
undefined8 uVar1;
undefined8 extraout_RDX;
int64_t iVar2;
int32_t *piVar3;
uint32_t uVar4;
int64_t in_FS_OFFSET;
int32_t iStack20;
int64_t iStack16;
iStack16 = *(int64_t *)(in_FS_OFFSET + 0x28);
piVar3 = &iStack20;
sym.imp.__isoc99_scanf(0x2004, piVar3);
if (iStack20 - 5U < 2) {
uVar4 = 0x200c;
sym.imp.puts("succeed");
} else {
uVar4 = 0x2007;
sym.imp.puts("fail");
}
if (iStack16 != *(int64_t *)(in_FS_OFFSET + 0x28)) {
sym.imp.__stack_chk_fail();
sym._init();
iVar2 = 0;
do {
uVar1 = (**(code **)(segment.LOAD3 + iVar2 * 8))((uint64_t)uVar4, piVar3, extraout_RDX);
iVar2 = iVar2 + 1;
} while (iVar2 != 1);
return uVar1;
}
return 0;
}
这似乎只检查 iStack20 < 7,并且 b.out 运行 没问题。
我不明白它是如何工作的。
在这里,一个整数下溢被利用了。运算
iStack20 - 5U
执行 unsigned 由于两个操作数类型不同时的隐式转换规则(C 标准 6.3.1.8(1)):
Otherwise [signedness differs], if the operand that has unsigned integer type has rank greater or
equal to the rank of the type of the other operand, then the operand with
signed integer type is converted to the type of the operand with unsigned
integer type.
因此,如果 iStack20
< 5,则结果将非常大(接近 UINT_MAX
),因此比较
iStack20 - 5U < 2
将是错误的。如果 iStack20
> 6,那么结果无论如何都会大于 2。所以保留了程序逻辑。
我 运行正在 linux 5.4.18-1-MANJARO 上安装 gcc 版本 9.2.0 (GCC)
文件名:a.c
#include<stdio.h>
int
main(void) {
int a;
scanf("%d", &a);
if (a < 5 || a > 6)
puts("fail");
else
puts("succeed");
}
那我运行:
gcc a.c -O0 -o a.out
gcc a.c -O1 -o b.out
我用r2反编译了a.out,得到了这个
undefined8 main(void)
{
undefined8 uVar1;
int64_t in_FS_OFFSET;
int32_t var_ch;
int64_t canary;
canary = *(int64_t *)(in_FS_OFFSET + 0x28);
sym.imp.__isoc99_scanf(0x2004, &var_ch);
if ((var_ch < 5) || (6 < var_ch)) {
sym.imp.puts("fail");
} else {
sym.imp.puts("succeed");
}
uVar1 = 0;
if (canary != *(int64_t *)(in_FS_OFFSET + 0x28)) {
uVar1 = sym.imp.__stack_chk_fail();
}
return uVar1;
}
这是我所期望的。
但是当我反编译时b.out,我得到了这个
undefined8 main(void)
{
undefined8 uVar1;
undefined8 extraout_RDX;
int64_t iVar2;
int32_t *piVar3;
uint32_t uVar4;
int64_t in_FS_OFFSET;
int32_t iStack20;
int64_t iStack16;
iStack16 = *(int64_t *)(in_FS_OFFSET + 0x28);
piVar3 = &iStack20;
sym.imp.__isoc99_scanf(0x2004, piVar3);
if (iStack20 - 5U < 2) {
uVar4 = 0x200c;
sym.imp.puts("succeed");
} else {
uVar4 = 0x2007;
sym.imp.puts("fail");
}
if (iStack16 != *(int64_t *)(in_FS_OFFSET + 0x28)) {
sym.imp.__stack_chk_fail();
sym._init();
iVar2 = 0;
do {
uVar1 = (**(code **)(segment.LOAD3 + iVar2 * 8))((uint64_t)uVar4, piVar3, extraout_RDX);
iVar2 = iVar2 + 1;
} while (iVar2 != 1);
return uVar1;
}
return 0;
}
这似乎只检查 iStack20 < 7,并且 b.out 运行 没问题。
我不明白它是如何工作的。
在这里,一个整数下溢被利用了。运算
iStack20 - 5U
执行 unsigned 由于两个操作数类型不同时的隐式转换规则(C 标准 6.3.1.8(1)):
Otherwise [signedness differs], if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.
因此,如果 iStack20
< 5,则结果将非常大(接近 UINT_MAX
),因此比较
iStack20 - 5U < 2
将是错误的。如果 iStack20
> 6,那么结果无论如何都会大于 2。所以保留了程序逻辑。