Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LF_ENUMERATE Not Parsed Correctly in example #88

Open
KawaiiNahida opened this issue Oct 27, 2024 · 3 comments
Open

LF_ENUMERATE Not Parsed Correctly in example #88

KawaiiNahida opened this issue Oct 27, 2024 · 3 comments

Comments

@KawaiiNahida
Copy link

KawaiiNahida commented Oct 27, 2024

Hi, I was experienced LF_ENUMERATE value not being parsed correctly with example code. the byte size pdb stores values does not always match the underlying type.
图片

@MolecularMatters
Copy link
Owner

Thank you for the report.
Can you be more specific about what's happening? What are the green and red markers supposed to indicate in the screenshot?
Can you make the PDB available for investigation?

@KawaiiNahida
Copy link
Author

The function DisplayEnumerates(const PDB::CodeView::TPI::Record* record, uint8_t underlyingTypeSize) always reads enum values using underlyingTypeSize. However, in my case, the compiler stores values in varying sizes. The green markers indicate the actual values, while the red markers represent CodeView::TPI::TypeRecordKind.

The current code produces the following output:

enum _DOT11_PHY_TYPE {
    dot11_phy_type_unknown = 0
    dot11_phy_type_any = 0
    dot11_phy_type_fhss = 1
    dot11_phy_type_dsss = 2
    dot11_phy_type_irbaseband = 3
    dot11_phy_type_ofdm = 4
    dot11_phy_type_hrdsss = 5
    dot11_phy_type_erp = 6
    dot11_phy_type_ht = 7
    dot11_phy_type_vht = 8
    dot11_phy_type_dmg = 9
    dot11_phy_type_he = 10
    dot11_phy_type_eht = 11
    dot11_phy_type_IHV_start = 32771
    dot11_phy_type_IHV_end = 32768
}

You may refer to this pdb file Test.zip

@KawaiiNahida
Copy link
Author

KawaiiNahida commented Oct 29, 2024

I'm posing the code I'm currently using. It's a bit messy, but it produces the correct values

inline uint8_t getLeafSize(PDB::CodeView::TPI::TypeRecordKind kind) {
    if (kind < PDB::CodeView::TPI::TypeRecordKind::LF_NUMERIC) return 0;
    switch (kind) {
    case PDB::CodeView::TPI::TypeRecordKind::LF_CHAR:
        return sizeof(uint8_t);
    case PDB::CodeView::TPI::TypeRecordKind::LF_USHORT:
    case PDB::CodeView::TPI::TypeRecordKind::LF_SHORT:
        return sizeof(uint16_t);
    case PDB::CodeView::TPI::TypeRecordKind::LF_LONG:
    case PDB::CodeView::TPI::TypeRecordKind::LF_ULONG:
        return sizeof(uint32_t);
    case PDB::CodeView::TPI::TypeRecordKind::LF_QUADWORD:
    case PDB::CodeView::TPI::TypeRecordKind::LF_UQUADWORD:
        return sizeof(uint64_t);
    }
    return 0;
}

void adaptEnumFields(
    const pdb_utils::TypeTable&       typeTable,
    const PDB::CodeView::TPI::Record* record,
    tinyast::EnumType&                enumType,
    int                               enumSize,
    bool                              enumSigned
) {
    using namespace PDB::CodeView::TPI;
    const char* leafName    = nullptr;
    auto        maximumSize = record->header.size - sizeof(uint16_t);

    for (size_t i = 0; i < maximumSize;) {
        auto fieldRecord = reinterpret_cast<const PDB::CodeView::TPI::FieldList*>(
            reinterpret_cast<const uint8_t*>(&record->data.LF_FIELD.list) + i
        );
        const static std::set<TypeRecordKind> validRecordKinds = {
            TypeRecordKind::LF_INDEX,
            TypeRecordKind::LF_ENUMERATE,
        };
        if (!validRecordKinds.contains(fieldRecord->kind)) {
            // Other kinds of records are not implemented
            throw std::runtime_error(fmt::format("unexpected record kind {}", (int)fieldRecord->kind));
        }

        if (fieldRecord->kind == TypeRecordKind::LF_ENUMERATE) {

            auto        valueSize = pdb_utils::getLeafSize(fieldRecord->data.LF_ENUMERATE.lfEasy.kind, false);
            const char* valuePtr  = fieldRecord->data.LF_ENUMERATE.value;

            if (valueSize == 0) valueSize = 2; // if kind is invalid, assume 2 bytes
            else valuePtr += sizeof(PDB::CodeView::TPI::TypeRecordKind);

            leafName        = valuePtr + valueSize;
            uint64_t UValue = 0;
            int64_t  SValue = 0;

            auto readEnumValue = [&]<typename ST, typename UT, int size>() {
                if (valueSize == size) {
                    if (enumSigned) SValue = *reinterpret_cast<const ST*>(valuePtr);
                    else UValue = *reinterpret_cast<const UT*>(valuePtr);
                }
            };
            readEnumValue.operator()<int8_t, uint8_t, 1>();
            readEnumValue.operator()<int16_t, uint16_t, 2>();
            readEnumValue.operator()<int32_t, uint32_t, 4>();
            readEnumValue.operator()<int64_t, uint64_t, 8>();

            if (enumSigned) UValue = *reinterpret_cast<uint64_t*>(&SValue);

            // (int)0xFFFFFFFF -> (PDB)0xFF -> (val)0xFFFFFFFFFFFFFFFF
            auto mask = (1ULL << (enumSize * 8)) - 1;
            enumType.EnumValues.emplace_back(UValue & mask, leafName);

            i += static_cast<size_t>(leafName - reinterpret_cast<const char*>(fieldRecord));
            i += strnlen(leafName, maximumSize - i - 1) + 1;
            i  = (i + (sizeof(uint32_t) - 1)) & (0 - sizeof(uint32_t));
        } else if (fieldRecord->kind == TypeRecordKind::LF_INDEX) {
            adaptEnumFields(
                typeTable,
                typeTable.getTypeRecord(fieldRecord->data.LF_INDEX.type),
                enumType,
                enumSize,
                enumSigned
            );
            i += sizeof(FieldList::Data::LF_INDEX);
            i  = (i + (sizeof(uint32_t) - 1)) & (0 - sizeof(uint32_t));
            continue;
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants