Obliczanie stałych i odpowiedniki instrukcji

Obliczanie stałych i odpowiedniki instrukcji
Bartosz Wójcik
  • Rejestracja:około 14 lat
  • Ostatnio:ponad 4 lata
  • Postów:439
0

Rozwijam generator metamorficzny do swojego oprogramowania, jest to biblioteka, która bierze na wejściu binarny kod x86, analizuje go i tworzy zmutowaną (polimorficzną) kopię tego kodu, która w domyśle ma utrudniać jego analizę w deasemblerach oraz debuggerach. Efekty pracy są naprawdę ciekawe, z jednej instrukcji assemblera potrafi wygenerować nawet 1 MB wyjściowy kod o dokładnie takiej samej funkcjonalności.

Często szukam interesujących rozwiązań, które mógłbym dodać do biblioteki. Jednym z elementów silnika jest zamiana konkretnych instrukcji na ich odpowiedniki.

Przykład - zerowanie rejestru procesora x86

Kopiuj
; proste rozwiazania
xor eax,eax

sub eax,eax

and eax,0

lea eax,[0]

; bardziej rozbudowane
or   eax,-1
not eax

or   eax,-1
xor eax,-1

and eax,7FFFFFFFh
sar  eax,31

or  eax,-1
inc eax

Czy znacie może jakieś interesujące algorytmy, które pozwalałyby uzyskiwać stałe wartości (0, 1, -1 etc.), właśnie przez zastępowanie oczywistych instrukcji serią innych, równoznacznych.

Druga rzecz czy znacie jakieś kreatywne metody na uzyskanie funkcjonalności podstawowych instrukcji:

  1. XOR A, B
  2. AND A, B
  3. OR A,B
  4. NOT A
  5. NEG A
  6. ADD A, B
  7. SUB A, B
  8. CMP A, B
  9. TEST A, B
  10. INC A
  11. DEC A

Mam całkiem dużo różnych kombinacji dla sporej gamy instrukcji, jednak jak pisałem wyżej, zawsze poszukuję nowych i interesujących rozwiązań.

msm
Administrator
  • Rejestracja:prawie 16 lat
  • Ostatnio:4 miesiące
6
Kopiuj
; proste rozwiazania
xor eax,eax
 
sub eax,eax
 
and eax,0
 
lea eax,[0]
 
; bardziej rozbudowane
or   eax,-1
not eax
(...)

Nie wiem czy robisz specjalne checki na to, ale przecież w ogólności takie podstawienie jest niepoprawne:

  • NOT i LEA nie modyfikują flag
  • XOR i AND zmieniają CF, OF, PF, SF i ZF, a stan AF jest undefined
  • SUB zmienia AF, CF, OF, PF, SF, ZF jak leci.

W większości "typowego" kodu generowanego przez kompilator wszystko będzie ok bo większość rzeczy polega na samym ZF tuż przed skokiem, ale i tak ryzykownie.

Czy znacie może jakieś interesujące algorytmy, które pozwalałyby uzyskiwać stałe wartości (0, 1, -1 etc.), właśnie przez zastępowanie oczywistych instrukcji serią innych, równoznacznych.

Może rekurencyjnie? Skoro już masz kod do podmieniania normalnych wyrażeń, powinno się go dać zastosować elegancko również do generowania stałych.

(Uwaga: to poniżej to miał być 30-linijkowy POC. Nie wyszło, udało mi się powstrzymać dopiero po 130 linijkach. Jakoś tak szybko sie pisało)

Kopiuj
import random
from numbers import Number

def add(a, b, t):
    t -= 1
    if t < 0:
        return 'add', a, b
    return sub(var(a, t), neg(var(b, t), t), t)

def sub(a, b, t):
    t -= 1
    if t < 0:
        return 'sub', a, b
    return add(var(a, t), neg(var(b, t), t), t)

def neg(a, t):
    t -= 1
    choice = random.randint(0, 2)
    if t < 0:
        return 'neg', a
    if choice == 0:
        return mul(var(a, t), -1, t)
    return sub(0, var(a, t), t)

def const(a, t):
    n = random.randint(0, 2**30)
    t -= 1
    choice = random.randint(0, 6)
    if t < 0:
        return a
    if choice == 0:
        return sub(a+n, n, t)
    if choice == 1:
        return add(a-n, n, t)
    if choice == 2 and a % 2 == 0:
        return add(a, a, t)
    if choice == 3:
        return xorb(a ^ n, n, t)
    if choice == 4:
        return orb(a & 0xAAAA, a & 5555, t)
    return neg(-a, t)

def var(a, t):
    if isinstance(a, Number) and random.choice([True, False]):
        return const(a, t)
    t -= 1
    choice = random.randint(0, 7)
    if t < 0:
        return a
    if choice == 0:
        return sub(a, 0, t)
    if choice == 1:
        return add(a, 0, t)
    if choice == 2:
        return mul(a, 1, t)
    if choice == 3:
        return xorb(a, 0, t)
    if choice == 4:
        return andb(a, a, t)
    if choice == 5:
        return orb(a, a, t)
    if choice == 6:
        return orb(a, 0, t)
    return neg(neg(a, t), t)
    
def mul(a, b, t):
    def expand(c, a, t):
        if c <= 0:
            return 0
        print c, a, t
        return add(a, expand(c-1, a, t-1), t-1)
    t -= 1
    if t < 0:
        return 'mul', a, b
    if isinstance(a, Number) and a < 10 and a > 2:
        return expand(a, b, t)
    if isinstance(b, Number) and b < 10 and b > 2:
        return expand(b, a, t)
    return 'mul', var(b, t), var(a, t)

def xorb(a, b, t):
    t -= 1
    if t < 0:
        return 'xor', a, b
    return andb(orb(var(a, t), var(b, t), t), notb(andb(var(a, t), var(b, t), t), t), t)

def andb(a, b, t):
    t -= 1
    choice = random.randint(0, 2)
    if t < 0:
        return 'and', a, b
    if choice == 0:
        return notb(orb(notb(var(a, t), t), notb(var(b, t), t), t), t)
    return andb(var(b, t), var(a, t), t)

def orb(a, b, t):
    t -= 1
    choice = random.randint(0, 2)
    if t < 0:
        return 'or', a, b
    if choice == 0:
        return notb(andb(notb(var(a, t), t), notb(var(b, t), t), t), t)
    return andb(var(b, t), var(a, t), t)

def notb(a, t):
    t -= 1
    choice = random.randint(0, 1)
    if t < 0:
        return 'not', a
    return xorb(var(a, t), 0xFFFFFFFF, t)
    
def generate(operation):
    if isinstance(operation, Number) or isinstance(operation, basestring):
        return [('push', str(operation))]
    result = []
    for operand in operation[1:]:
        result += generate(operand)
    if len(operation) == 2:
        result.append(('pop', 'eax'))
        result.append((operation[0], 'eax'))
    elif len(operation) == 3:
        result.append(('pop', 'eax'))
        result.append(('pop', 'ebx'))
        result.append((operation[0], 'eax', 'ebx'))
    else:  # hack, we do not support operations with > 2 operands
        raise Exception('not supported')
    return result

def dump(code):
    for opcode in generate(code):
        print ' '.join(opcode)

Najpierw rozwijamy każde wyrażenie rekurencyjnie, aż do osiągnięcia jakiegoś limitu (tutaj ograniczenie to zmienna t przekazywana ciągle, czyli maksymalne zagnieżdżenie rekurencji), a później za pomoca stosu wyliczamy jego wartość.

Przykładowo:

Kopiuj
>>> add('eax', 'ebx', 4)
('add', ('and', ('add', ('not', ('or', ('not', ('and', ('add', 0, 0), ('and', 'eax', 'eax'))), ('not', ('xor', ('not', ('or', ('not', ('xor', 'eax', 0)), ('not', ('or', 0, 0)))), 4294967295L)))), ('neg', 0)), ('add', ('not', ('or', ('not', ('and', ('add', 0, 0), ('and', 'eax', 'eax'))), ('not', ('xor', ('not', ('or', ('not', ('xor', 'eax', 0)), ('not', ('or', 0, 0)))), 4294967295L)))), ('neg', 0))), ('neg', ('sub', 0, ('add', ('add', 0, ('neg', ('or', ('sub', 0, ('sub', 0, ('sub', ('neg', 0), ('neg', ('sub', 0, ('mul', ('and', ('sub', ('or', 'ebx', 'ebx'), ('neg', ('sub', 0, ('sub', 0, 0)))), ('sub', ('or', 'ebx', 'ebx'), ('neg', ('sub', 0, ('sub', 0, 0))))), 1)))))), 0))), 0))))
>>> const(444, 4)
('add', ('and', ('sub', 0, -848737611), ('sub', 0, -848737611)), ('neg', ('sub', 0, ('sub', ('mul', -1, ('or', ('mul', 1, 848737167), ('mul', 1, 848737167))), 0))))
>>> const(444, 4)
('and', ('sub', ('and', ('sub', ('sub', -411900607, ('neg', 679890536)), 0), ('or', ('sub', 0, -267989525), 0)), 0), ('or', ('and', ('or', ('or', ('xor', ('not', ('and', ('not', ('xor', ('or', ('sub', -5011564, ('neg', 273001493)), 0), 4294967295L)), ('not', ('xor', ('mul', ('sub', 47352906, ('neg', 220636619)), 1), 4294967295L)))), 4294967295L), 0), 4294967295L), ('not', ('and', ('or', ('xor', ('not', ('and', ('not', ('xor', ('or', ('sub', -5011564, ('neg', 273001493)), 0), 4294967295L)), ('not', ('xor', ('mul', ('sub', 47352906, ('neg', 220636619)), 1), 4294967295L)))), 4294967295L), 0), 4294967295L))), ('and', ('or', ('or', ('xor', ('not', ('and', ('not', ('xor', ('or', ('sub', -5011564, ('neg', 273001493)), 0), 4294967295L)), ('not', ('xor', ('mul', ('sub', 47352906, ('neg', 220636619)), 1), 4294967295L)))), 4294967295L), 0), 4294967295L), ('not', ('and', ('or', ('xor', ('not', ('and', ('not', ('xor', ('or', ('sub', -5011564, ('neg', 273001493)), 0), 4294967295L)), ('not', ('xor', ('mul', ('sub', 47352906, ('neg', 220636619)), 1), 4294967295L)))), 4294967295L), 0), 4294967295L)))))
>>> dump(andb('eax', 'ebx', 2))
push eax
push 1
pop eax
pop ebx
mul ebx eax
push eax
push ebx
push 0
pop eax
pop ebx
or ebx eax
push eax
pop eax
pop ebx
and ebx eax
push eax
>>> dump(const(666, 3))
push -772936222
push 985105622
pop eax
pop ebx
add ebx eax
push eax
push 8202
push 4114
pop eax
pop ebx
or ebx eax
push eax
push -1
pop eax
pop ebx
mul ebx eax
push eax
pop eax
neg eax
push eax
pop eax
pop ebx
sub ebx eax
push eax

W ten sposób z prostego dodawania/stałej możemy zrobić dowolnie skomplikowany kod.

---KONIEC POSTA---

OFFTOPIC:
Podstawowy problem z takim zabezpieczeniem to na pewno to, że jest to jak najbardziej odwracalne. Wystarczy np. wczytać taki megabajtowy ASM, wyeksportować np. do asemblera llvm, uruchomić kompilator z maksymalną optymalizacja i odebrać gotowy wynik.

Z ciekawości na ile praktyczna jest taka deobfuskacja, zrobiłem sobie eksport do C (nie chciało mi się bawić z konwerowaniem wyjścia do LLVM więc eksport z poziomu wyrażenia:)

Kopiuj
def export_c(operation):
    if isinstance(operation, Number) or isinstance(operation, basestring):
        return str(operation)
    return '{0}({1})'.format(operation[0], ','.join(export_c(x) for x in operation[1:]))

export_c(const(666, 3))

Eksportujemy sobie dowolne wyrażenie do C, a później kompilujemy:

Kopiuj
#include <stdint.h>
#include <stdio.h>

uint32_t add(uint32_t a, uint32_t b) { return a + b; }
uint32_t sub(uint32_t a, uint32_t b) { return a - b; }
uint32_t mul(uint32_t a, uint32_t b) { return a * b; }
uint32_t and(uint32_t a, uint32_t b) { return a & b; }
uint32_t or(uint32_t a, uint32_t b) { return a | b; }
uint32_t xor(uint32_t a, uint32_t b) { return a ^ b; }
uint32_t neg(uint32_t a) { return -a; }
uint32_t not(uint32_t a) { return ~a; }

int main() {
    printf("%d", sub(or(add(add(0,neg(neg(neg(add(0,neg(neg(neg(sub(or(0,0),neg(mul(or(and(0,-666),and(0,-666)),-1))))))))))),neg(0)),add(add(0,neg(neg(neg(add(0,neg(neg(neg(sub(or(0,0),neg(mul(or(and(0,-666),and(0,-666)),-1))))))))))),neg(0))),neg(sub(0,and(add(0,neg(or(add(sub(sub(0,0),neg(sub(0,neg(neg(add(sub(add(add(and(mul(0,-1),mul(0,-1)),neg(mul(sub(add(0,neg(or(sub(add(sub(sub(xor(sub(-881620716,neg(881621382)),0),neg(sub(0,mul(add(0,neg(and(and(or(0,0),not(and(0,0))),and(or(0,0),not(and(0,0)))))),1)))),0),neg(sub(0,or(0,0)))),neg(0)),sub(add(sub(sub(xor(sub(-881620716,neg(881621382)),0),neg(sub(0,mul(add(0,neg(and(and(or(0,0),not(and(0,0))),and(or(0,0),not(and(0,0)))))),1)))),0),neg(sub(0,or(0,0)))),neg(0))))),0),-1))),0),neg(sub(0,neg(0)))),neg(0))))))),neg(0)),add(sub(sub(0,0),neg(sub(0,neg(neg(add(sub(add(add(and(mul(0,-1),mul(0,-1)),neg(mul(sub(add(0,neg(or(sub(add(sub(sub(xor(sub(-881620716,neg(881621382)),0),neg(sub(0,mul(add(0,neg(and(and(or(0,0),not(and(0,0))),and(or(0,0),not(and(0,0)))))),1)))),0),neg(sub(0,or(0,0)))),neg(0)),sub(add(sub(sub(xor(sub(-881620716,neg(881621382)),0),neg(sub(0,mul(add(0,neg(and(and(or(0,0),not(and(0,0))),and(or(0,0),not(and(0,0)))))),1)))),0),neg(sub(0,or(0,0)))),neg(0))))),0),-1))),0),neg(sub(0,neg(0)))),neg(0))))))),neg(0))))),add(0,neg(or(add(sub(sub(0,0),neg(sub(0,neg(neg(add(sub(add(add(and(mul(0,-1),mul(0,-1)),neg(mul(sub(add(0,neg(or(sub(add(sub(sub(xor(sub(-881620716,neg(881621382)),0),neg(sub(0,mul(add(0,neg(and(and(or(0,0),not(and(0,0))),and(or(0,0),not(and(0,0)))))),1)))),0),neg(sub(0,or(0,0)))),neg(0)),sub(add(sub(sub(xor(sub(-881620716,neg(881621382)),0),neg(sub(0,mul(add(0,neg(and(and(or(0,0),not(and(0,0))),and(or(0,0),not(and(0,0)))))),1)))),0),neg(sub(0,or(0,0)))),neg(0))))),0),-1))),0),neg(sub(0,neg(0)))),neg(0))))))),neg(0)),add(sub(sub(0,0),neg(sub(0,neg(neg(add(sub(add(add(and(mul(0,-1),mul(0,-1)),neg(mul(sub(add(0,neg(or(sub(add(sub(sub(xor(sub(-881620716,neg(881621382)),0),neg(sub(0,mul(add(0,neg(and(and(or(0,0),not(and(0,0))),and(or(0,0),not(and(0,0)))))),1)))),0),neg(sub(0,or(0,0)))),neg(0)),sub(add(sub(sub(xor(sub(-881620716,neg(881621382)),0),neg(sub(0,mul(add(0,neg(and(and(or(0,0),not(and(0,0))),and(or(0,0),not(and(0,0)))))),1)))),0),neg(sub(0,or(0,0)))),neg(0))))),0),-1))),0),neg(sub(0,neg(0)))),neg(0))))))),neg(0))))))))));
}
Kopiuj
$ gcc test.c -O3
$ ./a.exe
666

Wynik kompilacji:

Kopiuj
mov     [esp+10h+var_C], 29Ah
mov     [esp+10h+var_10], offset aD ; "%d"
call    printf

:(.

Ok, ale to w sumie nie był poziom asemblera. Postanowiłem więc wskoczyć na poziom niżej, i przeprowadzić porządną podmianę asma na maszynę rejestrową. Dalej byłem zbyt leniwy na odpalanie LLVM, ale to również można w C zasymulować:
(ten kod to już kompletny hack, z "czystym kodem" nie ma wiele wspólnego. Ale kto powiedział że PoCe mają być ładne)

Kopiuj
def export_c_opcodes(opcodes):
    stack = []
    eax, ebx = '', ''
    for i, opcode in enumerate(opcodes):
        val = None
        if opcode[0] == 'push':
            stack.append('var_' + str(i))
            val = opcode[1]
        elif opcode[0] == 'pop':
            val = stack.pop()
            if opcode[1] == 'eax':
                eax = val
            else:
                ebx = val
            continue
        else:
            val = '{0}({1})'.format(opcode[0], ','.join(str(x) for x in opcode[1:]))
        val = val.replace('eax', eax)
        val = val.replace('ebx', ebx)
        print 'uint32_t var_{0} = {1};'.format(i, val)
        eax = 'var_' + str(i)
    print 'uint32_t result = ' + stack.pop() + ';'

export_c_opcodes(generate(const(666, 4)))

(tutaj warto zauważyć że to co dostaje export_c_opcodes jest na poziomie abstrakcji asemblera, i dokonuje translacji do c).

export_c_opcodes generuje nam takie cudo:

Kopiuj
#include <stdint.h>
#include <stdio.h>

uint32_t add(uint32_t a, uint32_t b) { return a + b; }
uint32_t sub(uint32_t a, uint32_t b) { return a - b; }
uint32_t mul(uint32_t a, uint32_t b) { return a * b; }
uint32_t and(uint32_t a, uint32_t b) { return a & b; }
uint32_t or(uint32_t a, uint32_t b) { return a | b; }
uint32_t xor(uint32_t a, uint32_t b) { return a ^ b; }
uint32_t neg(uint32_t a) { return -a; }
uint32_t not(uint32_t a) { return ~a; }

int main() {
    uint32_t eax, ebx;
    // <autogenerated>
    uint32_t var_0 = 694626396;
    uint32_t var_1 = 694626396;
    uint32_t var_4 = and(var_0,var_1);
    uint32_t var_5 = var_4;
    uint32_t var_6 = 1;
    uint32_t var_9 = mul(var_5,var_6);
    uint32_t var_10 = var_9;
    uint32_t var_11 = 0;
    uint32_t var_12 = 0;
    uint32_t var_13 = 694625730;
    uint32_t var_15 = not(var_13);
    uint32_t var_16 = var_15;
    uint32_t var_17 = 694625730;
    uint32_t var_19 = not(var_17);
    uint32_t var_20 = var_19;
    uint32_t var_23 = or(var_16,var_20);
    uint32_t var_24 = var_23;
    uint32_t var_26 = not(var_24);
    uint32_t var_27 = var_26;
    uint32_t var_28 = 0;
    uint32_t var_31 = add(var_27,var_28);
    uint32_t var_32 = var_31;
    uint32_t var_34 = neg(var_32);
    uint32_t var_35 = var_34;
    uint32_t var_38 = add(var_12,var_35);
    uint32_t var_39 = var_38;
    uint32_t var_40 = 0;
    uint32_t var_41 = 694625730;
    uint32_t var_43 = not(var_41);
    uint32_t var_44 = var_43;
    uint32_t var_45 = 694625730;
    uint32_t var_47 = not(var_45);
    uint32_t var_48 = var_47;
    uint32_t var_51 = or(var_44,var_48);
    uint32_t var_52 = var_51;
    uint32_t var_54 = not(var_52);
    uint32_t var_55 = var_54;
    uint32_t var_56 = 0;
    uint32_t var_59 = add(var_55,var_56);
    uint32_t var_60 = var_59;
    uint32_t var_62 = neg(var_60);
    uint32_t var_63 = var_62;
    uint32_t var_66 = add(var_40,var_63);
    uint32_t var_67 = var_66;
    uint32_t var_70 = and(var_39,var_67);
    uint32_t var_71 = var_70;
    uint32_t var_74 = sub(var_11,var_71);
    uint32_t var_75 = var_74;
    uint32_t var_77 = neg(var_75);
    uint32_t var_78 = var_77;
    uint32_t var_81 = add(var_10,var_78);
    uint32_t var_82 = var_81;
    uint32_t result = var_82;
    // </autogenerated>
    printf("%d", result);
}
Kopiuj
$ gcc test.c -O3
$ ./a.exe
666

I wynik:

Kopiuj
mov     [esp+10h+var_C], 29Ah
mov     [esp+10h+var_10], offset aD ; "%d"
call    printf

:>.

Więc jeśli chodzi o obfuskację, można z tego wyciągnąć wniosek, że z tak zmutowanym przez maszynę kodem, może poradzić sobie również maszyna (tzn. wrócić do bardziej cywilizowanej postaci). No chyba że uda się "ogłupić" kompilator rzeczami których nie będzie potrafił zoptymalizować (ale kompilatory z czasem coraz mądrzejsze).
Większą skuteczność takie rozwiązanie osiągnie pewnie przeciwko silnikom antywirusów, ale na heurystykę nie pomoże (a i tak już nic nie działa wyłacznie na podstawie patternów/fingerprintów).

edytowany 1x, ostatnio: msm
Shalom
Tyle pracy a kompialtor wszystko zepsuł! :P
Azarien
daj volatile do zmiennych. nie ma prawa zoptymalizować.
msm
ALe no rzecz w tym że kompilator sam potrafi takie podmiany zoptymalizować, nie chodzi o to żeby mu to uniemożliwiać.
Bartosz Wójcik
  • Rejestracja:około 14 lat
  • Ostatnio:ponad 4 lata
  • Postów:439
0

Po pierwsze dziękuję za odpowiedź, mało kto chyba zajmuje się takimi problemami i rozumie specyfikę tego typu rozwiązań.

msm napisał(a):

W większości "typowego" kodu generowanego przez kompilator wszystko będzie ok bo większość rzeczy polega na samym ZF tuż przed skokiem, ale i tak ryzykownie.

Akurat ta kwestia jest inaczej rozwiązana, jednak flagi w tym wypadku można zachować (pushfd + popfd) lub wymusić ich ustawienie (or) przed wyjściem z bloku zmutownaych instrukcji.

msm napisał(a):

Najpierw rozwijamy każde wyrażenie rekurencyjnie, aż do osiągnięcia jakiegoś limitu (tutaj ograniczenie to zmienna t przekazywana ciągle, czyli maksymalne zagnieżdżenie rekurencji), a później za pomoca stosu wyliczamy jego wartość.

Bardzo fajne rozwiązanie, ja unikam instrukcji mul/div etc. ze względu na prędkość ich wykonywania. Stosowanie MUL/DIV można fajnie rozwiązać implementując zastępcze algorytmy do mnożenia i dzielenia bazujące na shiftach i instrukcjach LEA.

msm napisał(a):

Podstawowy problem z takim zabezpieczeniem to na pewno to, że jest to jak najbardziej odwracalne. Wystarczy np. wczytać taki megabajtowy ASM, wyeksportować np. do asemblera llvm, uruchomić kompilator z maksymalną optymalizacja i odebrać gotowy wynik.

To o czym pisałem, czyli rozwijanie pojedynczych instrukcji to tylko skrawek mojego zabezpieczenia. Dochodzi do tego generowanie pseudo-procedur dla bloków kodu, wielu ścieżek z różnymi mutacjami tych samych instrukcji czy instrukcje zaśmiecające korzystające z analizy danego bloku kodu, więc nie jest to robione na "pałę", do tego oczywiście instrukcje utrudniające debuggowanie (tzw. junki) i kilka innych metod utrudniających analizę.

msm napisał(a):

Z ciekawości na ile praktyczna jest taka deobfuskacja, zrobiłem sobie eksport do C (nie chciało mi się bawić z konwerowaniem wyjścia do LLVM więc eksport z poziomu wyrażenia:)
...
Ok, ale to w sumie nie był poziom asemblera. Postanowiłem więc wskoczyć na poziom niżej, i przeprowadzić porządną podmianę asma na maszynę rejestrową. Dalej byłem zbyt leniwy na odpalanie LLVM, ale to również można w C zasymulować:
(ten kod to już kompletny hack, z "czystym kodem" nie ma wiele wspólnego. Ale kto powiedział że PoCe mają być ładne)
...
Więc jeśli chodzi o obfuskację, można z tego wyciągnąć wniosek, że z tak zmutowanym przez maszynę kodem, może poradzić sobie również maszyna (tzn. wrócić do bardziej cywilizowanej postaci). No chyba że uda się "ogłupić" kompilator rzeczami których nie będzie potrafił zoptymalizować (ale kompilatory z czasem coraz mądrzejsze).
Większą skuteczność takie rozwiązanie osiągnie pewnie przeciwko silnikom antywirusów, ale na heurystykę nie pomoże (a i tak już nic nie działa wyłacznie na podstawie patternów/fingerprintów).

Masz rację co do tych optymalizacji, że kompilator w tym przypadku sobie poradzi.

Jednak to zupełnie inna bajka, jeśli engine mutujący jest stosowany jako element innych zabezpieczeń, dla przykładu używam go do mutacji kolejnych warstw silnika polimorficznego (czyli jest to funkcja szyfrująca dane/kod i jednocześnie generująca kod dekryptora), który działa tak, że docelowy zmutowany kod jest deszyfrowany np. przez 100 warstw (taka deszyfrująca matrioszka), jedna po drugiej i każda zmutowana, i prawie na samym końcu wykonywany jest ten ostateczny kod, który jest np. silnikiem VM, który dopiero ostatecznie wykonuje czy emuluje działanie kodu, który ma chronić.

Widziałem już kilka wtyczek dla OllyDbg czy IDA, czy samodzielnych "deobfuscatorów", jednak w starciu z rzeczywistością ich funkcjonowanie było bardzo mocno ograniczone, a wręcz kulawe. Silniki mutujące są też różne, np. niektóre bazują jedynie na operacjach na stosie i stworzenie poprawnego deobfuscatora jest po prostu cholernie trudne, a jeszcze jak masz to w zabezpieczonej aplikacji ze sprawdzaniem sum kontrolnych kodu (jakby np. ktoś wpadł na pomysł, żeby dynamicznie "oczyścić" kod i go śledzić), z metodami antydebug, z wielowątkowością warstw zabezpieczających (które współpracują ze sobą np. przez system zdarzeń) to można się załamać psychicznie :)

Jakbyś miał jakieś inne pomysły jak skomplikować ten proces lub widzisz w tym co napisałem jakieś niedociągnięcia to chętnie wysłucham.

Pozdrawiam

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)