Användarvisningsbild
ttt
Respekterad Medlem
Inlägg: 121
Blev medlem: 19 jan 2015, 01:27

x86-virtualiserare

23 aug 2015, 14:52

Hej,

har snickrat på en obfuskerare för x86 ett litet tag nu. Den kommer primärt att bygga på virtualisering av den kod som ska skyddas. Detta innebär att native-kod översätts till bytekod för en minimal virtuell maskin som bakas in i det skyddade programmet.

(OBS. "VM" och "virtuell maskin" kommer användas i en väldigt liberal mening i den här tråden, det är en minimal implementation vi talar om)

Jag har hittills bara lagt grunden till virtualiseraren och VM:et, men planen är att fortsätta pilla på detta tills det kan utmana de existerande lösningarna som finns idag.

Ett exempel på hur det kan se ut:

Kod: Markera allt

6a24            | push 00000024h
687f104000      | push 0040107Fh
6854104000      | push 00401054h
6a00            | push 00000000h
ff15b8114000    | call dword ptr [004011B8h]
83f806          | cmp eax, 06h
7533            | jne 0040104Ch
6a00            | push 00000000h
6a00            | push 00000000h
6a00            | push 00000000h
6895104000      | push 00401095h
ff15d8114000    | call dword ptr [004011D8h]
6a00            | push 00000000h
6a00            | push 00000000h
6a00            | push 00000000h
68a2104000      | push 004010A2h
ff15d8114000    | call dword ptr [004011D8h]
6a00            | push 00000000h
6a00            | push 00000000h
6a00            | push 00000000h
68b8104000      | push 004010B8h
ff15d8114000    | call dword ptr [004011D8h]
6a00            | push 00000000h
ff155c114000    | call dword ptr [0040115Ch]


Översatt till bytekod (lite tråkigt exempel kanske med tanke på den nära 1:1-mappningen, kan lägga upp något mer avancerat sen)

Kod: Markera allt

vm_push 0000000b
vm_push 00000003
mul
vm_push 00000003
add
push_dword
vm_push 00001277
vm_push 00000378
mul
vm_push 000003b7
add
push_dword
vm_push 00001277
vm_push 00000378
mul
vm_push 0000038c
add
push_dword
vm_push 00000000
push_dword
native
native
push_flag 00000040
vm_push 00000040
xor
jump_rel_c 0000008a
vm_push 00000000
push_dword
vm_push 00000000
push_dword
vm_push 00000000
push_dword
vm_push 00001277
vm_push 00000378
mul
vm_push 000003cd
add
push_dword
native
vm_push 00000000
push_dword
vm_push 00000000
push_dword
vm_push 00000000
push_dword
vm_push 00001277
vm_push 00000378
mul
vm_push 000003da
add
push_dword
native
vm_push 00000000
push_dword
vm_push 00000000
push_dword
vm_push 00000000
push_dword
vm_push 00001277
vm_push 00000378
mul
vm_push 000003f0
add
push_dword
native
vm_push 00000000
push_dword
native
vm_push 0040100a
vm_exit

Som synes är det väldigt stack-orienterat. CISC-instruktioner expanderas för att minimera logiken som krävs i VM (scaled addressing är ett bra exempel: mov eax, [ecx+ebx*2+cafebabe] => push ebx, push 2, mul, push ecx, add, push cafebabe, add, pop eax). Conditional jumps expanderas också för att täcka in samtliga variationer med endast en handler (xor används i exemplet ovan för att kontrollera ZF). "native" innebär att instruktionen inte virtualiseras, utan exekveras på stacken (skalkod-esque).


Tilltänkta funktioner:
  • Fullkomligt stöd för alla vanliga IA32-instruktioner. Enklare än det låter, då endast de instruktioner som har sidoeffekter utanför VM:ets kontroll behöver virtualiseras.
  • Obfuskering av VM. Det är i nuläget väldigt enkelt att kartlägga vad varje del av VM:et gör och mappa handlers till opcodes. Mutering och obfuskering på x86-nivå krävs för att försvåra detta. Handlers kan brytas upp, och den synliga fetch-decode-loopen (väldigt frestande att BP:a) skulle kunna göras om enligt indirect threading eller liknande
  • Obfuskering på VM-nivå. Mappa flera virtuella opcodes till samma handlers (slumpning av opcodes är redan implementerat). Infoga virtuell skräpkod.
  • Virtualisering av VM, dvs. i två nivåer. Detta i kombination med obfuskering på x86-nivån blir nog lagom kul overhead.
  • Kryptering av strängar. Något enkelt XOR:ande bara, så de inte är direkt synliga i binären.
Stöd för x64 ligger lite längre fram i tiden...


Tar gärna emot förslag på förbättring eller allmänna tips. :)



Exemplet som visades ovan finns här (.exe):
https://mega.nz/#!UoJxiDBI!UhlB86oGvlK- ... Alg2EoKPvg

Originalfilen är här:
https://mega.nz/#!k9hExZRQ!HtbzHeTSDndK ... XFDtPlFhI4

Jag tog ett av exemplen som följer med fasm som är safe att köra. När programmet startas visas en MessageBox som frågar om du vill ha plats till öl. Trycker man Ja öppnas CD-luckan (höhöhö), trycker man nej avslutas programmet.

Användarvisningsbild
red
Respekterad Medlem
Inlägg: 303
Blev medlem: 08 nov 2014, 14:06
Områden: Programmering

Re: x86-virtualiserare

23 aug 2015, 15:13

Väldigt intressant projekt! Finns det på Github eller liknande?

Användarvisningsbild
ttt
Respekterad Medlem
Inlägg: 121
Blev medlem: 19 jan 2015, 01:27

Re: x86-virtualiserare

23 aug 2015, 21:49

red skrev:Väldigt intressant projekt! Finns det på Github eller liknande?

Ingenting än, nej. Binären för verktyget kommer definitivt komma upp här, och ev. ett SDK för en lib-variant till GitHub. Håller på större delen av källkoden tills vidare då det kommer vara en hel del security through obscurity involverat.

edit: Ser att mitt exempel i TS blev fel.

mov eax, [ecx+ebx*2+cafebabe] => push ebx, push 2, mul, push ecx, add, push cafebabe, add, pop eax).

Detta är bara sant om det varit LEA, inte MOV

Användarvisningsbild
red
Respekterad Medlem
Inlägg: 303
Blev medlem: 08 nov 2014, 14:06
Områden: Programmering

Re: x86-virtualiserare

23 aug 2015, 21:53

ttt skrev:Ingenting än, nej. Binären för verktyget kommer definitivt komma upp här, och ev. ett SDK för en lib-variant till GitHub. Håller på större delen av källkoden tills vidare då det kommer vara en hel del security through obscurity involverat.


Jag tror att du får mer "uppmärksamhet" om du FOSSar det. Blir säkrare i längden om du släpper koden då andra människor börjar contributa.

Användarvisningsbild
likvidera
Respekterad Medlem
Inlägg: 444
Blev medlem: 15 okt 2014, 15:29
Områden: Exploits, Reversing
Kontakt: Twitter

Re: x86-virtualiserare

23 aug 2015, 23:12

Nice, när du lagt till lite mer obfs så kan de bli en fin crackme ;)
CyberPirate - keybase.io/likvidera

Användarvisningsbild
ttt
Respekterad Medlem
Inlägg: 121
Blev medlem: 19 jan 2015, 01:27

Re: x86-virtualiserare

30 aug 2015, 15:08

Liten uppdatering (lite dagbok-igt, men det kanske är någon som uppskattar)

Lade till stöd för absoluta jumps och calls, vilket borde vara ett av de sista "specialfallen" som måste hanteras.

Relativa jumps och calls är ju enkla att räkna om genom att bara titta på hur längden mellan source och destination förändrats (de nya instruktionerna har olika längd, plus att de oftast är betydligt fler). Jumps och calls till absoluta adresser är svårare eftersom det oftast inte går att räkna på dessa i förväg. Det är också klurigt att försöka koda dessa direkt i instruktionerna eftersom de inte nödvändigtvis är konstanta, t.ex en indirekt adress:

mov eax, [0xc0dec0de]
jmp eax

eller

call dword [0xc0dec0de]

Här är det svårt att sia om vad [0xc0dec0de] innehåller innan denna instruktion nås. Är det ett anrop via IAT och ett värde som fyllts i när processen startades? Ett värde som ändrats betydligt senare? etc.

Löste detta genom att programmet varnar var dessa instruktioner förekommer. Användaren uppmanas därefter ange de adresser som dessa jumps eller calls kan landa på. De nya adresserna räknas därefter ut och sparas i en tabell där de kan slås upp under körning (först när hoppet sker vet vi exakt var det kommer landa). Lite meckigare för användaren, men det fungerar.

red skrev:Jag tror att du får mer "uppmärksamhet" om du FOSSar det. Blir säkrare i längden om du släpper koden då andra människor börjar contributa.


"uppmärksamhet" - möjligtvis. Säkrare i längden? Det skulle jag nog inte hävda. Att det kan bli bättre med fler deltagare och större kompetens - givetvis. Men öppen källkod som kan användas som referens när ett program ska avobfuskeras? Njäääää. Se bara på existerande program och de automatiska "devirtualizers" (i det här fallet) som dykt upp. Med kryptografiska implementationer t.ex. där man ska kunna sitta med ritningen i hand och ändå inte kunna bräcka dem är det en helt annan visa.

likvidera skrev:Nice, när du lagt till lite mer obfs så kan de bli en fin crackme ;)

Det är en bit dit, men en crackme för att sätta allt på prov är ju ett måste. :p

Användarvisningsbild
ttt
Respekterad Medlem
Inlägg: 121
Blev medlem: 19 jan 2015, 01:27

Re: x86-virtualiserare

14 dec 2015, 23:28

Julledigt och lite tid över till diverse projekt

- gjorde om hela VM-biten. Tog bort mellanlagringen i TEB (som var en dum lösning) och den exekverbara stacken (dummare). Allokerar fortfarande en egen stack. Kikade lite på liknande kommersiella verktyg som istället lägger kontexten på ordinarie stack och skapar extrautrymme där. Föredrog att inte göra så för att stödja kod som tittar förbi sin egen stack frame
- bitvisa och logiska instruktioner är nu implementerade med en NAND-handler
- lade till enkel register swapping


Det blir lagom marigt overhead nu:

Kod: Markera allt

entry_point:
   mov eax, 0x12345678
   mov ecx, 0xbaadf00d
   xor eax, ecx
   jnz exit
   and eax,0
exit:
   push 0
   call [ExitProcess]


blir till

Kod: Markera allt

00401000->00401000 /.../                           | <reg_swap> { 01, 05, 02, 07, 06, 03, 00, 04 }
00401000->00401020 026c180000                      | vm_push 0000186c
00401000->00401025 02d3be0000                      | vm_push 0000bed3
00401000->0040102a 10                              | mul
00401000->0040102b 02740d0000                      | vm_push 00000d74
00401000->00401030 0f                              | add
00401000->00401031 0601                            | pop_reg 01
00401005->00401033 02f6250000                      | vm_push 000025f6
00401005->00401038 02ecea0400                      | vm_push 0004eaec
00401005->0040103d 10                              | mul
00401005->0040103e 0245150000                      | vm_push 00001545
00401005->00401043 0f                              | add
00401005->00401044 0605                            | pop_reg 05
0040100a->00401046 0401                            | push_reg 01
0040100a->00401048 0405                            | push_reg 05
0040100a->0040104a 09                              | nand
0040100a->0040104b 0401                            | push_reg 01
0040100a->0040104d 09                              | nand
0040100a->0040104e 0401                            | push_reg 01
0040100a->00401050 0405                            | push_reg 05
0040100a->00401052 09                              | nand
0040100a->00401053 0405                            | push_reg 05
0040100a->00401055 09                              | nand
0040100a->00401056 09                              | nand
0040100a->00401057 0601                            | pop_reg 01
0040100c->00401059 0740000000                      | push_flag 00000040
0040100c->0040105e 0202000000                      | vm_push 00000002
0040100c->00401063 0220000000                      | vm_push 00000020
0040100c->00401068 10                              | mul
0040100c->00401069 11                              | xor
0040100c->0040106a 0c11000000                      | jump_rel_c 00000011
0040100e->0040106f 0401                            | push_reg 01
0040100e->00401071 0200000000                      | vm_push 00000000
0040100e->00401076 09                              | nand
00a7020e->00401077 03                              | vm_repush
0040100e->00401078 09                              | nand
0040100e->00401079 0601                            | pop_reg 01
00401011->0040107b 0200000000                      | vm_push 00000000
00401011->00401080 05                              | push_dword
00401013->00401081 0008ff15482040009090            | native
0040108b->0040108b /.../                           | <reg_swap> { 01, 05, 02, 07, 06, 03, 00, 04 }
00000000->004010ab 025c0e0000                      | vm_push 00000e5c
00000000->004010b0 0276040000                      | vm_push 00000476
00000000->004010b5 10                              | mul
00000000->004010b6 02a2010000                      | vm_push 000001a2
00000000->004010bb 0f                              | add
00000000->004010bc 01                              | vm_exit

(innan slumpning av opcodes)

Listar man ut varje handler och kommer så långt som utskriften ovan är det däremot lätt att sätta ihop dem efter mönster. Skräpkod ska fortfarande till, men därefter borde nog någon form av interleaving baserat på sidoeffekter implementeras. :/

Användarvisningsbild
ttt
Respekterad Medlem
Inlägg: 121
Blev medlem: 19 jan 2015, 01:27

Re: x86-virtualiserare

01 maj 2016, 22:32

Glömde bort att uppdatera, men jag har nog mer eller mindre avslutat det här projektet, i alla fall mot den riktning jag hade tänkt att det skulle gå ursprungligen. Blir nog någon annan spinn på det om jag fortsätter (en packer för att enbart hindra signaturmatchning?). En guide eller halv öppensåsning kommer nog också.

Bifogar om någon vill testa.

Användning
1. Länka mot libphant.lib*, använd funktionerna i libphant.h (eller infoga byte pattern direkt om ni inte använder VS, det står i filen vad det ska vara) för att markera kod som ska skyddas.

*programmet kan också manuellt kopiera koden inuti libphant.lib till sista sektionen i målfilen, men detta är inte helt uppdaterat (imports och lite tabellvärden måste patchas in manuellt), så undvik.

Kod: Markera allt

#include "libphant.h"
int main(int argc, const char *argv[])
{
    BeginProtect;
    // grejor här
    EndProtect;
}


sen kör phant.exe med

  • input_file="path to the input exe"
  • output_file="path to the output exe"

    För att lägga till adresser för uppslagning i körtid (se inlägg ovan om när det behövs):
  • va_target="virtual address" (hexadecimalt, inget prefix eller suffix)

Relocations var på G men gjorde ej klart, så det stöds ej. Tänkte bara lösa dessa genom en handler "rebase" som infogas t.ex. innan PushDword i exemplen ovan, och sedan bara lägga till den om adressen för värdet ifråga finns i relocation table.

Instruktioner med konstiga prefix fungerar eventuellt. Instruktioner med size prefix annat än DWORD stöds inte utan exekveras som de är. Några av de mer obskyra jcc:na är inte implementerade heller.

Finns ett exempelprogram i zippen också, samt binärer för före- och efter.

05eb169e362f41a4c164b73228d322171bdb46b3.zip
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.

Användarvisningsbild
ttt
Respekterad Medlem
Inlägg: 121
Blev medlem: 19 jan 2015, 01:27

Re: x86-virtualiserare

11 maj 2016, 23:12

Sluttjötat om detta nu

ttt skrev:öppensåsning

https://github.com/chvrn/phantasm-x86-virtualizer

Användarvisningsbild
Chloë
Administratör
Inlägg: 2505
Blev medlem: 08 okt 2014, 02:28
Områden: Webbsäkerhet
Kontakt: Twitter

Re: x86-virtualiserare

11 maj 2016, 23:19

Kan du inte skriva en writeup om den binär du postade i challenges? De var väl skyddad denna obfuskerare?
keybase.io/dotchloe | chloe.re | chloe.website