DFG

Sp1n

Мудрец
Сообщения
116
Реакции
72
Взялся кодить наследование данных, тормознулся продумать архитектуру. При тестах для него визора на вмпроте из темы рядом, насмотрелся на тьму ветвлений в логах и у меня появилась мысль решать тем же наследованием. Есть входные данные eg адрес строки, в результате нужно получить связанный" с ней код, eg ветвление после сравнения строк. Посторонних ветвлений не должно быть. Практически так - есть ввод ключа, известно в какой буфер он вводится, в результате автоматика определяет точное место проверки ключа. Что бы такое сделать и в частности отсечь холостые ветвления необходимо наследование данных(DFG) - механизм не сложный, но коденг его длительный, тк обработка таблиц опкодов. Иначе никак, код должен быть функцией искомых данных, например:

Код:
if L < N    ; Ссылка L содержащая искомые данные.
например:
            ; I() наследованиe(src -> dst).
    cmp L,N    ; Flags = I(L)
    setb R    ; R = I(F)
    cmp X,C    ; Flags ~ I(F)
    je        ; ~(je)
    jmp Ivt[R]    ; IP = I(R)
            ; Flow: L -> F -> R -> IP
или:
    cmp [L],N    ; Flags = I(L)
    jb        ; Ip = I(L)
            ; Flow: L -> F -> IP
В первом варианте это может быть вирта, тогда будет найден соотв её вектор. В идеале так - при вводе ключа определяются новые данные, затем через наследование выводится код их обработки.

Есть ли наработки, посмотреть что уже сделано или матчасть ?
 

mak

Соломенные сандалии
Администратор
Сообщения
956
Реакции
1 028
Есть ли наработки, посмотреть что уже сделано или матчасть ?
Я думаю, что PFG это частный случай DFG, в обоих случаях наработок нет, так как PFG обычно только в компиляторах применяли (что не исключает динамического использования), а DFG зависит от движка, на котором работает дизасм, плюс от того, как построены структуры движка. Классический вид во многих CFG, где DFG часть CFG (работают в связке), можно посмотреть в старом плагине с ехелаба, в хидере ariadne.h, где есть структуры для описания "Ячейки Памяти":
C++:
//! Identifier
struct SAIRId
{
    DWORD   m_family;   //!< Family
    DWORD   m_level;    //!< Level
    DWORD   m_id;       //!< Identifier
    DWORD   m_id1;      //!< Additional identifier 1
    DWORD   m_id2;      //!< Additional identifier 2
};

//! AIR instruction argument
struct SAIRArgument
{
    struct SAIRId       m_corr_id;          //!< Id of the corresponding memory cell or instruction
    struct SAIRId       m_sec_corr_id;      //!< Secondary id (used for branch instructions)
    DWORD64             m_imm;              //!< Immediate value
    DWORD               m_deref_size;       //!< Dereference size ( used for the pointer dereferencing )
    WORD                m_type;             //!< Argument type
    WORD                m_sec_type;         //!< Secondary type
};

//! AIR instruction
struct SAIRInstruction
{
    struct SAIRArgument m_args[3];          //!< Arguments
    struct SAIRId       m_id;               //!< Instruction id
    BOOL                m_has_children;     //!< TRUE if an instruction is composite
    BOOL                m_has_parent;       //!< TRUE if an instruction has a parent
    DWORD               m_reserved[4];      //!< Reserved ( don't overwrite!!! )
    BYTE                m_type;             //!< Instruction type
};

//! AIR memory cell
struct SAIRMc
{
    struct SAIRId       m_id;                       //!< Memory cell id
    LONG                m_offs;                     //!< Memory cell offset
    DWORD               m_size;                     //!< Memory cell size
    BOOL                m_independent;              //!< TRUE if a memory cell is independent
    BOOL                m_has_children;             //!< TRUE if a memory cell is composite
    BOOL                m_has_parent;               //!< TRUE if a memory cell has a parent
    DWORD               m_reserved[4];              //!< Reserved ( don't overwrite!!! )
};

//! IR basic blocks
struct SAIRBb
{
    struct SAIRId       m_first;                    //!< Id of the first instruction
    struct SAIRId       m_second;                   //!< Id of the last instruction
    INT                 m_index;                    //!< Index of the basic block
    DWORD               m_reserved[4];              //!< Reserved ( don't overwrite!!! )
};
Сама ячейка описывается своим состоянием, вопрос лишь, хочешь ли ты разделить на части CFG, DFG, PFG.

По теме PFG тоже самое, конкретных наработок нет, да и вместо DBI был раньше просто отладчик.
Начал кодить наследование(pfg), без этого компонента далее ничего не будет. Вот как это выглядит практически:

pfg.png

Так можно к примеру мусор из кода вычищать. Был недавно для этого проект, tetrane etc, автор имхо сумасшедший такое юзать)
Немного теории по PFG:
In computer science, pointer analysis, or points-to analysis, is a static code analysis technique that establishes which pointers, or heap references, can point to which variables, or storage locations. It is often a component of more complex analyses such as escape analysis. A closely related technique is shape analysis.
(This is the most common colloquial use of the term. A secondary use has pointer analysis be the collective name for both points-to analysis, defined as above, and alias analysis. Points-to and alias analysis are closely related but not always equivalent problems.)
Context-Insensitive, Flow-Insensitive Algorithms - Pointer analysis algorithms are used to convert collected raw pointer usages (assignments of one pointer to another or assigning a pointer to point to another one) to a useful graph of what each pointer can point to.
Steensgaard's algorithm and Andersen's algorithm are common context-insensitive, flow-insensitive algorithms for pointer analysis.
https://en.wikipedia.org/wiki/Pointer_analysis
https://en.wikipedia.org/wiki/Escape_analysis
https://en.wikipedia.org/wiki/Steensgaard's_algorithm

Steensgaard, Bjarne (1996). "Points-to analysis in almost linear time"
Andersen, Lars Ole (1994). Program Analysis and Specialization for the C Programming Language (PDF)

Dynamic Points-To Sets: A Comparison with Static Analyses and Potential Applications in Program Understanding and Optimization
Andersen flow-insensitive pointer analysis

Generalized Points-to Graphs: A New Abstraction of Memory in the Presence of Pointers
https://arxiv.org/abs/1801.09189
https://github.com/xiaoqi35/Pointer-analysis-for-C-language

Steensgaard's algorithm
Steensgaard's algorithm is based on equality constraints, as opposed to Andersen's algorithm, which is based on subset constraints. This allows points-to information to be tracked using a union-find data structure. This choice gives the algorithm its characteristic speed; when implemented using a union-find data structure it is linear space and almost linear time in the size of the input program.

Disjoint-set data structure - Disjoint Sets using union by rank and path compression Graph Algorithm
https://en.wikipedia.org/wiki/Disjoint-set_data_structure
In computer science, a disjoint-set data structure, also called a union–find data structure or merge–find set, is a data structure that stores a collection of disjoint (non-overlapping) sets. Equivalently, it stores a partition of a set into disjoint subsets. It provides operations for adding new sets, merging sets (replacing them by their union), and finding a representative member of a set. The last operation makes it possible to find out efficiently if any two elements are in the same or different sets.

Lock-free parallel disjoint set data structure (aka UNION-FIND) with path compression and union by rank
DISJOINT-SET, UNION BY RANK & PATH COMPRESSION
https://www.geeksforgeeks.org/disjoint-set-data-structures/
An efficient wait-free implementation of a concurrent disjoint-set data structure - C implementation of a wait-free disjoint-set data structure (aka Union-Find) Based on paper by Jayanti and Tarjan:
DISJOINT-SET-OPERATIONS - Implemented Union-Find algorithm By Rank and Path Compression using height balanced AVL tree without any limitation of space. Time Complexity for this algorothm is O(log(~n~)) and implemented set operations of AVL tree using split and join algorithm. Time complexity O(m.log(n/m + 1)) where m and n are size of two AVL tree.

Ещё один пример компоновки - https://chriscummins.pythonanywhere.com/ или https://github.com/ChrisCummins/ProGraML
PROGRAML: A Graph-based Program Representation for Data Flow Analysis and Compiler Optimizations

В переносе на DBI ничего нет, что было ожидаемо ..
 

Прикрепленные файлы:

Sp1n

Мудрец
Сообщения
116
Реакции
72
mak

> Я думаю, что PFG это частный случай DFG
> вопрос лишь, хочешь ли ты разделить на части CFG, DFG, PFG.

Так и есть, rip содержит указатель, разница между рон в особенностях загрузки его - ветвления. CFG частный случай DFG для rip, тогда переход данные с кодом связывает DFG(rip). Это значит что если при наследовании приемник данных rip(или иначе IS для rip), то это ветвление фукция данных, что и нужно.

Странно что нет ничего, наследование(dfg) это ведь первое что нужно для автомат. анализа. Например маркирнуть данные и определить куда они далее расходятся. Не удивительно что всякую защиту вручную разбирают, был бы двиг, это делала бы автоматика..
 

Sp1n

Мудрец
Сообщения
116
Реакции
72
Первую таблицу опкодов почти доделал, но даже так результаты впечатляют o_O

df.png
 

Sp1n

Мудрец
Сообщения
116
Реакции
72
Китайцы использовали дата флоу для детекта эксплойтов. Изменения памяти находили в дампах вм, execution state" как я понял.
 

Прикрепленные файлы:

Sp1n

Мудрец
Сообщения
116
Реакции
72
Тестовый код:

masm:
Testp proc uses ebx
Local U:UNICODE_STRING
Local Hash:ULONG
Local Buffer[32]:CHAR
    .repeat
        invoke StdIn, addr Buffer, 32
    .until Eax >= 6
    and B Buffer[eax][-2],0
    invoke RtlComputeCrc32, 0, addr Buffer, Eax
    .if Eax == 1528330Ah
        invoke RtlCreateUnicodeStringFromAsciiz, addr U, addr Buffer
        mov edx,U.Buffer
        mov eax,D[edx]
        add eax,D[edx][4]
        .if Eax == 00F400F4H    ; zzzz
            invoke RtlHashUnicodeString, addr U, TRUE, 0, addr Hash
            .if Hash != 8810ED00h
                lea eax,U
                %DBG "%wZ..", Eax
                nop
            .endif
        .endif
    .endif
    ret
Testp endp
Маркернут первый байт в буфере:

Код:
Sequence(Ip):
    if Ip = @ReadFile
    IpRead = [T.Esp]
    DtRead = [T.Esp][8]
    if Ip = IpRead
        SetM8(DtRead)
    fi
Выхлоп такой:

dt.png
- PF это индексация данных в массивах через маркернутую часть адреса.
 

Sp1n

Мудрец
Сообщения
116
Реакции
72
Практические результаты. Для теста взял консольные крэкми(что бы пока окна не трогать), которые картировались ранее, сабж без ошибок решает:
1.PNG
2.PNG

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

В таком виде это уже не RE..
 

Прикрепленные файлы:

Последнее редактирование:

Sp1n

Мудрец
Сообщения
116
Реакции
72
Тестил есчо на крэкми, в общем решает оно все, на чем работает(не все опкоды описаны eg mul/div). Выше ошибки, вот последний билд.

sl6.png
sv3.png
Те картировались, этот я вроде не смотрел:
spnk.png

Интересен семпл с васм, антидамп на фаултах. На каждой инструкции срабатывает ловушка. Для наследования я добавил обработку, она общая - при фаулте происходит переключение задачи. Текущий контекст(CS) выгружается в память, соотв. память наследуется, контекст ресетится и наоборот после обработки фаулта.

Семпл был на слабо, результат кажется удивительным(знаю rel читает):

srel.png

Пока остановился на этом, меня не будет неопред время :(
 

Прикрепленные файлы:

  • Понравилось
Реакции: mak
Верх Низ