• XSS.stack #1 – первый литературный журнал от юзеров форума

Получение primary/secondary unmatched функций в BinDiff

varwar

El Diff
Забанен
Регистрация
12.11.2020
Сообщения
1 383
Решения
5
Реакции
1 537
Пожалуйста, обратите внимание, что пользователь заблокирован
Кто-нибудь в курсе как BinDiff реализует получение удаленных и добавленных функций? Непосредственно в самой базе .BinDiff я не нашел таблицы, где хранятся эти функции.

Пока решение сделал тупое, для новой и старой баз .i64 собираю все функции и делаю два списка. Метод рабочий, но костыльный.
Откуда-то же BinDiff берет имена и адреса этих функций.
 
Приветствую, постараюсь ответить на вопрос.
Глобально, любой исходный код может иметь соответсвоиший .PDB файл, в зависмости какой компайлер использывался.
Этот файл по сути хранить именную базу данных всех функций, переменных, структур и прочие данные находяешися коде.
Это позволяет проводить анализы дисассемблерами очень эфективно поскольку этот файл можно будет задружить вместе с загруженным экзешником.

В реверсинге где в 99% случиев когда .PDB файл экзешника не доступен можно применить следуйиший подход:
Можно использывать дисассемблеры такие как Ghidra или IDA Pro для того чтобы создать исходный код в виде языка C, даже если оригинал был написан на языке.

На этот моменте обьесню не большой нюанс.
Если оригинальный код был написан на другом языке от C, то из-за применяемой логикой на другом языке могут добавиться функции в новом исходном коде на C.
Они не влияют на основную логику кода они больше используются как стабилезаторы и компенсаторы.

Самый базовый пример это резерв и отпущения памяти в C где это выполняется в ручную по сравнению с языками где это происходит овтоматически.
В дисассемблере с языка как JAVA или Go на C, все фоновые функции будут добавлены с генернами именами. К примеру:

C:
unsigned func_1(struct_3* arg_1){
    unsigned var_1 = ...
    unsigned var_2 = ...
    ...
}

Вернёмся к основной теме.
Поскольку дисассемблер позволяет создать новый исходнный код, он так же потдаеться изменениям, то есть, появляется возможность изменения имён переменных, финкций и добавления кода.

В таком случии, потребуется анализировать новый исходный код, изменить генерние имена на существиные и можно будет скомпелировать новый код компайлером позволяющим создание .PDB файла.

C:
Before:

unsigned func_1(struct_3* arg_1){
    unsigned var_1 = ...
    unsigned var_2 = ...
    ...
}

OpCode:
...
PUSH EBX, [ESP]
JMP sample.000023ac
...

After:

unsigned encrypt (Message* message){
    unsigned Key = ...
    unsigned IV = ...
    ...
}

OpCode:
...
PUSH EBX, [ESP]
JMP sample.encrypt
...

После компиляции можно будет заредит новый скомпилирований код в BinDiff и зарядить .PDB файл.

При сровнений кода по BinDiff-у, особенно в похожих участках, можно на много луче понять функциональность.

Надеюсь я помог, если я где и ошибся или есть другая методика работы то пишите здесь.

Если есть вопросы можете написать в личку - постораюсь помочь :)
 
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
Приветствую, постараюсь ответить на вопрос.
Глобально, любой исходный код может иметь соответсвоиший .PDB файл, в зависмости какой компайлер использывался.
Этот файл по сути хранить именную базу данных всех функций, переменных, структур и прочие данные находяешися коде.
Это позволяет проводить анализы дисассемблерами очень эфективно поскольку этот файл можно будет задружить вместе с загруженным экзешником.

В реверсинге где в 99% случиев когда .PDB файл экзешника не доступен можно применить следуйиший подход:
Можно использывать дисассемблеры такие как Ghidra или IDA Pro для того чтобы создать исходный код в виде языка C, даже если оригинал был написан на языке.

На этот моменте обьесню не большой нюанс.
Если оригинальный код был написан на другом языке от C, то из-за применяемой логикой на другом языке могут добавиться функции в новом исходном коде на C.
Они не влияют на основную логику кода они больше используются как стабилезаторы и компенсаторы.

Самый базовый пример это резерв и отпущения памяти в C где это выполняется в ручную по сравнению с языками где это происходит овтоматически.
В дисассемблере с языка как JAVA или Go на C, все фоновые функции будут добавлены с генернами именами. К примеру:

C:
unsigned static func_1(struct_3* arg_1){
    unsigned var_1 = ...
    unsigned var_2 = ...
    ...
}

Вернёмся к основной теме.
Поскольку дисассемблер позволяет создать новый исходнный код, он так же потдаеться изменениям, то есть, появляется возможность изменения имён переменных, финкций и добавления кода.

В таком случии, потребуется анализировать новый исходный код, изменить генерние имена на существиные и можно будет скомпелировать новый код компайлером позволяющим создание .PDB файла.

C:
unsigned static encrypt (Message* message){
    unsigned Key = ...
    unsigned IV = ...
    ...
}

После компиляции можно будет заредит новый скомпилирований код в BinDiff и зарядить .PDB файл.

При сровнений кода по BinDiff-у, особенно в похожих участках, можно на много луче понять функциональность.

Надеюсь я помог, если я где и ошибся или есть другая методика работы то пишите здесь.

Если есть вопросы можете написать в личку - постораюсь помочь :)
Это все не то и не имеет отношения к описываемой проблеме. В общем-то о чем идет речь поймут те, кто работал с биндифом.
 
Это все не то и не имеет отношения к описываемой проблеме. В общем-то о чем идет речь поймут те, кто работал с биндифом.

Сожалею, возможно я не понял вопрос, не раз работал с BinDiff-ом и встречял разные проблемы.
Речь идёт тогда об импортных нативных функциях каторый код грузит во время рантайма?
Для нативных функций есть тоже база - логика одинаковая везде в этом смысле.
Например, похожим образом, x64dbg не умеет определить названия функций в нативних DLL файлах (в стате\ рантайме) без подходящий таблицы символов.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Сожалею, возможно я не понял вопрос, не раз работал с BinDiff-ом и встречял разные проблемы.
Речь идёт тогда об импортных нативных функциях каторый код грузит во время рантайма?
Для нативных функций есть тоже база - логика одинаковая везде в этом смысле.
Например, похожим образом, x64dbg не умеет определить названия функций в нативних DLL файлах (в стате\ рантайме) без подходящий таблицы символов.
Если коротко, то речь идет про эту функциональность.
В самой базе .BinDiff этих функций нет в таблице function. И вопрос в том, где же они находятся тогда и как BinDiff эти функции получает, если не из своей же базы.
В таблице function находятся те, что помечены в интерфейсе BinDiff как "Matched Functions", т.е. патченные и не патченные функции.

1748364380101.png

1748364304401.png
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Сожалею, возможно я не понял вопрос, не раз работал с BinDiff-ом и встречял разные проблемы.

как BinDiff реализует получение удаленных и добавленных функций?
Если я правильно понял суть вопроса. То нужно найти код в биндифе (в его исходних), который отвечает за логику сравнения функций. Где вот показывается simiarity 0.9 а так же каким образом он это делает узнает каким образом в коде была добавлена новая функция и какая была удалена из кодовой базы.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Где вот показывается simiarity 0.9
Вот по similarity BinDiff получает функции из таблицы function. Там нет функций с similarity = 0.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Вот по similarity BinDiff получает функции из таблицы function. Там нет функций с similarity = 0.
Там скорее и не будет функции similarity это просто в гуи текст так отрисован. А вот число которое выводится плавающие оно скорее по формуле расчитывается. Я к чему это вёл... это всё взаимосвязанно при анализе. И я скорее всего не так выразился и начал как то из далека...
Откуда-то же BinDiff берет имена и адреса этих функций.
Основная логика сравнения тут

Там вот есть файлики которые определяют хеш функции для сравнения, чтобы не парсить строку

И адреса

оно? Там же и primary есть.
 
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
Там скорее и не будет функции similarity это просто в гуи текст так отрисован.
Я имел в виду не название функции в сурках биндифа, а названия удаленных/добавленных функций с similarity (это название поля в базе .BinDiff)
Пример запроса для базы afd.BinDiff.

1748366883524.png


оно?
Придется изучать сурки.

Собственно проблема то в том, чтобы не гонять иду по несколько кругов, декомпилируя функции. На паре сотен баз это ощутимо. Идеальным решением было бы извлечение unmatched primary/secondary из .BinDiff базы по sql-запросу (как я это делаю для патченных фунок), но там бублик.
Python:
def get_patched_functions(bd_path):
    if not os.path.exists(bd_path):
        log_message(f"[ERROR] BinDiff DB not found: {bd_path}")
        return []

    with sqlite3.connect(bd_path) as conn:
        cur = conn.cursor()
        cur.execute("""
            SELECT name1, name2, address1, address2, similarity
              FROM function
             WHERE similarity < 1
        """)
        results = cur.fetchall()
    log_message(f"[INFO] Patched functions ({len(results)}): {results}")
    return results

....


        bd_path = get_bindiff_path(bin_name, bin_ver)
        if not bd_path:
            log_message("[ERROR] Could not resolve bindiff path, exiting")
            save_and_exit(1)

        patched = get_patched_functions(bd_path)
        if not patched:
            log_message("[INFO] No patched functions, nothing to decompile")
            save_and_exit(0)

        # phase 1: preload rows
        for n1, n2, a1, a2, sim in patched:
            DiffAnalysis.pre_init_func_table(bin_name, bin_ver, n1, n2, a1, a2, sim)

        # phase 2: decompile & compress each
        ida_paths = DiffManager.get_ida_paths(ABS_DB_PATH, bin_name, bin_ver)
        cur_idb, prev_idb = [os.path.join(EL_DIFF_APP, p) for p in ida_paths]

        for n1, n2, a1, a2, sim in patched:
            # choose address/column
            if idc.get_idb_path() == prev_idb:
                addr, col = (int(a1,16) if isinstance(a1,str) else a1), "old_code"
            else:
                addr, col = (int(a2,16) if isinstance(a2,str) else a2), "new_code"

            # skip if already done
            if func_blob_exists(bin_name, bin_ver, n1, col):
                log_message(f"[DEBUG] Skip {n1} ({col})")
                continue

            code = decompile_function(addr)
            if not code:
                log_message(f"[ERROR] Decomp failed for {n1}")
                continue

            gz = gzip.compress(code.encode())
            DiffAnalysis.add_func_blobs(bin_name, bin_ver, n1, gz, col)

        #phase 3: add new/deleted
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Я имел в виду не название функции в сурках биндифа, а названия удаленных/добавленных функций с similarity (это название поля в базе .BinDiff)
Пример запроса для базы afd.BinDiff.
Что-то я запутался.... Погоди. Вот ты говоришь

как BinDiff реализует получение удаленных и добавленных функций?
Я и говорю, чтобы тебе это понять тебе надо найти функцию в сорцах, которая отвечает за сравнение. Поняв её логику ты поймешь каким образов биндиф это делает.
Непосредственно в самой базе .BinDiff я не нашел таблицы, где хранятся эти функции.
Так , а как он будет о них знать до сравнения? он ведь динамически их сравнивает уже когда ты подготовил файлы.
Пока решение сделал тупое, для новой и старой баз .i64 собираю все функции и делаю два списка. Метод рабочий, но костыльный.
Далее на основе хеша сравниваешь функции по аналогии как у биндифа
Откуда-то же BinDiff берет имена и адреса этих функций.
После того как произошло сравнение .... у него и будет два списка удаленных и добавленных, так как в одном бинаре есть эти функции в другом их нет.

Вот к примеру


идентификация функций на основе их уникальных числовых "отпечатков", вычисляемых через простые числа.

Я так понимаю ты хочешь функционал биндифа перенести в свою тулзу, чтобы она парсила бинарь и говорила, вот тут у тебя появилась новая функция func_123, а тут ее удали . Адреса у них такие 0x401000 ....
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Так , а как он будет о них знать до сравнения?
Так база .BinDiff это уже результат сравнения двух баз .Binexport, она хранит практически всю информацию о двух бинарях в своих таблицах - адреса, базовые блоки, примененные алгоритмы и прочую ебалу.
Далее на основе хеша сравниваешь функции по аналогии как биндифа
Так а зачем, я это уже реализовал с idapython через получение списка всех функций для двух баз и просто их отфильтровав. Это работает, но для тулзы такое решение не очень т.к. провоцирует создание еще одного цикла с запуском ida в batch mode для декомпиляции.
Желательно получить всю информацию при однократном запуске IDA для каждого бинарника (на самом деле двукратном) а не троекратном-четырехкратном, потому что мы не можем получить все функции не открыв обе базы IDA.
Короче не хочу на себя брать лишнее процессорное время.
 
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
Вроде не так страшен черт оказался и все быстро обрабатывается. Можно еще даже сократить время, игнорируя декомпиляцию всяких Feature* функций, которые как по мне не несут смысловой нагрузки для ресерчера.

Весь цикл теперь выглядит так.
1748723109072.png
 

Вложения

  • 1748722991250.png
    1748722991250.png
    23.8 КБ · Просмотры: 9
Should the “missing addr on one side” be treated as the indicator for an unmatched func?

Приветствую, постараюсь ответить на вопрос.
Глобально, любой исходный код может иметь соответсвоиший .PDB файл, в зависмости какой компайлер использывался.
Этот файл по сути хранить именную базу данных всех функций, переменных, структур и прочие данные находяешися коде.
Это позволяет проводить анализы дисассемблерами очень эфективно поскольку этот файл можно будет задружить вместе с загруженным экзешником.

В реверсинге где в 99% случиев когда .PDB файл экзешника не доступен можно применить следуйиший подход:
Можно использывать дисассемблеры такие как Ghidra или IDA Pro для того чтобы создать исходный код в виде языка C, даже если оригинал был написан на языке.

На этот моменте обьесню не большой нюанс.
Если оригинальный код был написан на другом языке от C, то из-за применяемой логикой на другом языке могут добавиться функции в новом исходном коде на C.
Они не влияют на основную логику кода они больше используются как стабилезаторы и компенсаторы.

Самый базовый пример это резерв и отпущения памяти в C где это выполняется в ручную по сравнению с языками где это происходит овтоматически.
В дисассемблере с языка как JAVA или Go на C, все фоновые функции будут добавлены с генернами именами. К примеру:

C:
unsigned func_1(struct_3* arg_1){
    unsigned var_1 = ...
    unsigned var_2 = ...
    ...
}

Вернёмся к основной теме.
Поскольку дисассемблер позволяет создать новый исходнный код, он так же потдаеться изменениям, то есть, появляется возможность изменения имён переменных, финкций и добавления кода.

В таком случии, потребуется анализировать новый исходный код, изменить генерние имена на существиные и можно будет скомпелировать новый код компайлером позволяющим создание .PDB файла.

C:
Before:

unsigned func_1(struct_3* arg_1){
    unsigned var_1 = ...
    unsigned var_2 = ...
    ...
}

OpCode:
...
PUSH EBX, [ESP]
JMP sample.000023ac
...

After:

unsigned encrypt (Message* message){
    unsigned Key = ...
    unsigned IV = ...
    ...
}

OpCode:
...
PUSH EBX, [ESP]
JMP sample.encrypt
...

После компиляции можно будет заредит новый скомпилирований код в BinDiff и зарядить .PDB файл.

При сровнений кода по BinDiff-у, особенно в похожих участках, можно на много луче понять функциональность.

Надеюсь я помог, если я где и ошибся или есть другая методика работы то пишите здесь.

Если есть вопросы можете написать в личку - постораюсь помочь :)
Сожалею, возможно я не понял вопрос, не раз работал с BinDiff-ом и встречял разные проблемы.
Речь идёт тогда об импортных нативных функциях каторый код грузит во время рантайма?
Для нативных функций есть тоже база - логика одинаковая везде в этом смысле.
Например, похожим образом, x64dbg не умеет определить названия функций в нативних DLL файлах (в стате\ рантайме) без подходящий таблицы символов.

21.jpg
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Should the “missing addr on one side” be treated as the indicator for an unmatched func?
No, assume you have a deleted function somewhere in the middle. The memory layout will shift and the rest of functions will be interpreted as unmatched, but it's not true.
Already solved with additional comparison logic against list of functions stored in BinDiff database. Confirmed with BinDiff results.
But this solution missed added/deleted imported functions. So we need also to compare imported functions lists from each IDA database to get full added/deleted.
 
Последнее редактирование:
https://xssforum7mmh3n56inuf2h73hvhnzobi7h2ytb3gvklrfqm7ut3xdnyd.onion/attachments/108199/

Я человеку конечно не смог помочь, беру ответственность.
Но мем вышел железным )))
В любом случии я заценил.

Вроде не так страшен черт оказался и все быстро обрабатывается. Можно еще даже сократить время, игнорируя декомпиляцию всяких Feature* функций, которые как по мне не несут смысловой нагрузки для ресерчера.

Также рад видит что varwar всё же нашёл логику справиться с задачий.
 
No, assume you have a deleted function somewhere in the middle. The memory layout will shift and the rest of functions will be interpreted as unmatched, but it's not true.
Oh yeah, right.
Я человеку конечно не смог помочь, беру ответственность.

images.png
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх