bitdefender / bddisasm

bddisasm is a fast, lightweight, x86/x64 instruction decoder. The project also features a fast, basic, x86/x64 instruction emulator, designed specifically to detect shellcode-like behavior.
Apache License 2.0
888 stars 115 forks source link

AF is not handled #56

Closed brian763 closed 2 years ago

brian763 commented 2 years ago

In the ShemuSetFlags function of file bdshemu.c

//
// ShemuSetFlags
//
static void
ShemuSetFlags(
    SHEMU_CONTEXT *Context,
    ND_UINT64 Dst,
    ND_UINT64 Src1,
    ND_UINT64 Src2,
    ND_OPERAND_SIZE Size,
    ND_UINT8 FlagsMode
    )
{
    ND_UINT8 pfArr[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };

    // Mask the operands with their respective size.
    Dst = ND_TRIM(Size, Dst);
    Src1 = ND_TRIM(Size, Src1);
    Src2 = ND_TRIM(Size, Src2);

    if (FlagsMode == FM_SHL || FlagsMode == FM_SHR || FlagsMode == FM_SAR)
    {
        // Shift with 0 count does not affect flags.
        if (Src2 == 0)
        {
            return;
        }
    }

    // PF set if the first bytes has an even number of 1 bits.
    if ((pfArr[Dst & 0xF] + pfArr[(Dst >> 4) & 0xF]) % 2 == 0)
    {
        Context->Registers.RegFlags |= NDR_RFLAG_PF;
    }
    else
    {
        Context->Registers.RegFlags &= ~NDR_RFLAG_PF;
    }

    // ZF set if the result is zero.
    if (Dst == 0)
    {
        Context->Registers.RegFlags |= NDR_RFLAG_ZF;
    }
    else
    {
        Context->Registers.RegFlags &= ~NDR_RFLAG_ZF;
    }

    // SF is set if the sign flag is set.
    if (ND_GET_SIGN(Size, Dst) != 0)
    {
        Context->Registers.RegFlags |= NDR_RFLAG_SF;
    }
    else
    {
        Context->Registers.RegFlags &= ~NDR_RFLAG_SF;
    }

    // OF and CF are handled differently for some instructions.
    if (FM_LOGIC == FlagsMode)
    {
        // OF and CF are cleared on logic instructions.
        Context->Registers.RegFlags &= ~(NDR_RFLAG_OF | NDR_RFLAG_CF);
    }
    else if (FM_SHL == FlagsMode)
    {
        // CF is the last bit shifted out of the destination.
        if (ND_GET_BIT((Size * 8ULL) - Src2, Src1))
        {
            Context->Registers.RegFlags |= NDR_RFLAG_CF;
        }
        else
        {
            Context->Registers.RegFlags &= ~NDR_RFLAG_CF;
        }

        if (Src2 == 1)
        {
            if (ND_GET_BIT(Size * 8ULL - 1, Dst) ^ ND_GET_BIT(Size * 8ULL - Src2, Src1))
            {
                Context->Registers.RegFlags |= NDR_RFLAG_OF;
            }
            else
            {
                Context->Registers.RegFlags &= ~NDR_RFLAG_OF;
            }
        }
    }
    else if (FM_SHR == FlagsMode)
    {
        // CF is the last bit shifted out of the destination.
        if (ND_GET_BIT(Src2 - 1, Src1))
        {
            Context->Registers.RegFlags |= NDR_RFLAG_CF;
        }
        else
        {
            Context->Registers.RegFlags &= ~NDR_RFLAG_CF;
        }

        if (Src2 == 1)
        {
            if (ND_GET_BIT(Size * 8 - 1, Dst))
            {
                Context->Registers.RegFlags |= NDR_RFLAG_OF;
            }
            else
            {
                Context->Registers.RegFlags &= ~NDR_RFLAG_OF;
            }
        }
    }
    else if (FM_SAR == FlagsMode)
    {
        // CF is the last bit shifted out of the destination.
        if (ND_GET_BIT(Src2 - 1, Src1))
        {
            Context->Registers.RegFlags |= NDR_RFLAG_CF;
        }
        else
        {
            Context->Registers.RegFlags &= ~NDR_RFLAG_CF;
        }

        Context->Registers.RegFlags &= ~NDR_RFLAG_OF;
    }
    else
    {
        // Set CF.
        if ((FM_SUB == FlagsMode) && (Src1 < Src2))
        {
            Context->Registers.RegFlags |= NDR_RFLAG_CF;
        }
        else if ((FM_ADD == FlagsMode) && (Dst < Src1))
        {
            Context->Registers.RegFlags |= NDR_RFLAG_CF;
        }
        else
        {
            Context->Registers.RegFlags &= ~NDR_RFLAG_CF;
        }

        // Set OF.
        if (FM_SUB == FlagsMode)
        {
            if ((ND_GET_SIGN(Size, Src1) && !ND_GET_SIGN(Size, Src2) && !ND_GET_SIGN(Size, Dst)) ||
                (!ND_GET_SIGN(Size, Src1) && ND_GET_SIGN(Size, Src2) && ND_GET_SIGN(Size, Dst)))
            {
                Context->Registers.RegFlags |= NDR_RFLAG_OF;
            }
            else
            {
                Context->Registers.RegFlags &= ~NDR_RFLAG_OF;
            }
        }
        else if (FM_ADD == FlagsMode)
        {
            if (ND_GET_SIGN(Size, Src1) == ND_GET_SIGN(Size, Src2) && 
                ND_GET_SIGN(Size, Src1) != ND_GET_SIGN(Size, Dst))
            {
                Context->Registers.RegFlags |= NDR_RFLAG_OF;
            }
            else
            {
                Context->Registers.RegFlags &= ~NDR_RFLAG_OF;
            }
        }
    }
}
vlutas commented 2 years ago

Hello,

This is intended (i.e., not an oversight or a bug). We don't update the AF for any instruction. If you have specific use-cases where this is needed, please share them.

Thanks!