Encrypt code in exes (decrypt at runtime)

Share your advanced PureBasic knowledge/code with the community.
User avatar
pdwyer
Addict
Addict
Posts: 2813
Joined: Tue May 08, 2007 1:27 pm
Location: Chiba, Japan

Post by pdwyer »

Right!

It creates an interesting problem though doesn't it. Assuming you have some key how do you get it in there to decrypt? You probably don't want one key unlocks all apps but it'd be an admin nightmare to have to manage all the exe's separately (compile for each customer?).

I know that this is not being brought up as foolproof or anything but perhaps if we put a few heads together on the design we can improve it a bit as the base idea looks good, just still a little hard to use in real life
Paul Dwyer

“In nature, it’s not the strongest nor the most intelligent who survives. It’s the most adaptable to change” - Charles Darwin
“If you can't explain it to a six-year old you really don't understand it yourself.” - Albert Einstein
User avatar
DoubleDutch
Addict
Addict
Posts: 3220
Joined: Thu Aug 07, 2003 7:01 pm
Location: United Kingdom
Contact:

Post by DoubleDutch »

:shock:

It's just to stop script kiddies! Don't for one moment think that you will ever be able to create something that will defeat the big boys.
https://deluxepixel.com <- My Business website
https://reportcomplete.com <- School end of term reports system
Inf0Byt3
PureBasic Fanatic
PureBasic Fanatic
Posts: 2236
Joined: Fri Dec 09, 2005 12:15 pm
Location: Elbonia

Post by Inf0Byt3 »

Yup, DoubleDutch is totally right. The idea looks good on the paper hehe. The main bad thing about this is that the code is decrypted at runtime. That means that all the code between the labels is going to be visible to the cracker's eyes. So they can come up with a sort of loader to give control to your exe and then patch directly in memory. This, however, implemented with a good encryption algo can protect your app for a limited time from usual byte-patching.
None are more hopelessly enslaved than those who falsely believe they are free. (Goethe)
Inf0Byt3
PureBasic Fanatic
PureBasic Fanatic
Posts: 2236
Joined: Fri Dec 09, 2005 12:15 pm
Location: Elbonia

Post by Inf0Byt3 »

Major update (multi-label support, better encryption, masked labels by DoubleDutch):

Encryptor:

Code: Select all

;By Inf0Byt3, 10OCT07
;
;Based on code by Rings/Pdwyer/Franky/Wayne Diamond
;Mods/Fixes/Improvements by DoubleDutch/Thefool
;
;Free to use, credits appreciated
;It would be nice to improve it if you have time and if you want to share the knowledge
;
;Now with multi-label support ;)
;Don't forget to enable Inline ASM

Structure Block
 StartOffset.l
 EndOffset.l
 Length.l
EndStructure

Structure CharArray 
 char.c[0] 
EndStructure

Global NewList Blocks.Block()

Procedure BZ_CompareMemory(*Src, *Dst, Len)
  MOV esi, *Src
  MOV edi, *Dst
  MOV eax, Len
  MOV ecx, eax
  AND eax, 3
  SHR ecx, 2
  XOR ebx, ebx
  REPE CMPSD
  !JNE .not_eq
  MOV ecx, eax
  XOR ebx, ebx
  REPE CMPSB
  !JNE .not_eq
  ProcedureReturn 1
  !.not_eq:
  ProcedureReturn 0
EndProcedure

Procedure XorWithKey (sText.l, LenText.l, sKey.l, LenKey.l) 
  XOR ecx, ecx 
  MOV esi, sText 
  MOV edi, sKey 
  MOV edx, LenText 
  MOV ebp, LenKey 
  ADD ebp, esi 
  xornextbyte: 
  MOV al, [esi] 
  MOV bl, [edi] 
  XOR al, bl 
  MOV [esi], al 
  INC ecx 
  INC esi 
  INC edi 
  CMP esi, ebp 
  JGE l_xorcomplete 
  CMP ecx, edx 
  JGE l_xornextround 
  JMP l_xornextbyte 
  xornextround: 
  XOR ecx, ecx 
  SUB edi, edx 
  JMP l_xornextbyte 
  xorcomplete: 
EndProcedure 

Procedure BinSearch(*Object.CharArray, ObjectLength.l, StartOffset.l, *String.CharArray, StringLength.l)
  Dim SkipLength.l(255)
  For i = 0 To 255
   SkipLength(i) = StringLength + 1
  Next
  For i = 0 To StringLength -1
   SkipLength(*String\char[i]) = StringLength - i   
  Next     
  CurrentOffset.l = StartOffset
  MaxOffset.l = ObjectLength - StringLength
  While CurrentOffset <= MaxOffset
   If BZ_CompareMemory(*Object + CurrentOffset, *String, StringLength) = 1
    FoundPos = CurrentOffset + 1
    Break
  EndIf
  CurrentOffset + SkipLength(*Object\char[CurrentOffset + StringLength])
  Wend
  ProcedureReturn FoundPos
EndProcedure 

Procedure Callback(Message.s)

 Debug Message
 
 ProcedureReturn 1

EndProcedure

Procedure EncryptExecutablePieces(File.s,*Callback)
 
 BlockStr.s = Chr($eb)+Chr($06)+Chr($eb)+Chr($fc)+Chr($eb)+Chr($fa)+Chr($eb)+Chr($f8)
 BlockEnd.s = Chr($eb)+Chr($04)+Chr($eb)+Chr($04)+Chr($eb)+Chr($fc)+Chr($eb)+Chr($fc)
 
 EXE_Block_Key.s = "this is the enc key"
 
 If FileSize(File) = 0 Or FileSize(File) = -1
  CallFunctionFast(*Callback,"No file or empty file...")
  ProcedureReturn 0
 EndIf
 
 FLRH = OpenFile(#PB_Any,File)
 If IsFile(FLRH) 
  
  CallFunctionFast(*Callback,"Opened file...")
  
  ;Read the file in memory
  TotalLength = Lof(FLRH)
  *MainMem = AllocateMemory(TotalLength)
  ReadData(FLRH,*MainMem,TotalLength)
  
  ;Now search for the blocks that need encryption
  CallFunctionFast(*Callback,"Searching for blocks...")
  While Pos <= TotalLength
   EStart = BinSearch(*MainMem,TotalLength,Pos,@BlockStr,Len(BlockStr))
   EEnd = BinSearch(*MainMem,TotalLength,Pos,@BlockEnd,Len(BlockEnd))
   If EStart > 0 And EEnd > 0
    AddElement(Blocks())
    Blocks()\StartOffset = EStart+Len(BlockStr)-1
    Blocks()\EndOffset = EEnd-1
    Blocks()\Length = Blocks()\EndOffset-Blocks()\StartOffset
   Else
    Break
   EndIf
   Pos + (EEnd-Pos)
  Wend
  
  ;Read and encrypt the data
  If CountList(Blocks()) > 0
   CallFunctionFast(*Callback,Str(CountList(Blocks()))+" block(s) were found.")
   ForEach Blocks()
    CallFunctionFast(*Callback,"Block start at offset: "+Str(Blocks()\StartOffset))
    CallFunctionFast(*Callback,"Block end at offset: "+Str(Blocks()\EndOffset))
    CallFunctionFast(*Callback,"Block length: "+Str(Blocks()\Length))
    *MemArea = AllocateMemory(Blocks()\Length)
    FileSeek(FLRH,Blocks()\StartOffset)
    ReadData(FLRH,*MemArea,Blocks()\Length)
    
    XorWithKey (*MemArea, Blocks()\Length, @EXE_Block_Key, Len(EXE_Block_Key)) 

    FileSeek(FLRH,Blocks()\StartOffset)
    WriteData(FLRH,*MemArea,Blocks()\Length)
    CallFunctionFast(*Callback,"Block patched...")
    FreeMemory(*MemArea)
   Next
   CallFunctionFast(*Callback,"")
  Else
   CallFunctionFast(*Callback,"No blocks found...")
  EndIf
  CallFunctionFast(*Callback,"Closing file...")
  CloseFile(FLRH)
  CallFunctionFast(*Callback,"Ready...")
 Else
  ProcedureReturn -1
 EndIf

EndProcedure

EncryptExecutablePieces("C:\Test.exe",@Callback())
Test executable

Code: Select all

;By Inf0Byt3, 10OCT07
;
;Based on code by Rings/Pdwyer/Franky/Wayne Diamond
;Mods/Fixes/Improvements by DoubleDutch/Thefool
;
;Free to use, credits appreciated
;It would be nice to improve it if you have time and if you want to share the knowledge
;
;Now with multi-label support ;)
;Don't forget to enable Inline ASM

Procedure XorWithKey (sText.l, LenText.l, sKey.l, LenKey.l) 
  XOR ecx, ecx 
  MOV esi, sText 
  MOV edi, sKey 
  MOV edx, LenText 
  MOV ebp, LenKey 
  ADD ebp, esi 
  xornextbyte: 
  MOV al, [esi] 
  MOV bl, [edi] 
  XOR al, bl 
  MOV [esi], al 
  INC ecx 
  INC esi 
  INC edi 
  CMP esi, ebp 
  JGE l_xorcomplete 
  CMP ecx, edx 
  JGE l_xornextround 
  JMP l_xornextbyte 
  xornextround: 
  XOR ecx, ecx 
  SUB edi, edx 
  JMP l_xornextbyte 
  xorcomplete: 
EndProcedure 

Procedure.l Scramble(StartAddress,Length)
 EXE_Block_Key.s = "this is the enc key"
 Mode = #PAGE_READWRITE
 Result=VirtualProtect_(StartAddress,Length,Mode,@OrigMode)
 XorWithKey (StartAddress,Length,@EXE_Block_Key,Len(EXE_Block_Key))
 VirtualProtect_(StartAddress,Length,OrigMode,Mode)
 ProcedureReturn 1
EndProcedure

;The code address
CStart = ?S1
CEnd = ?E1
CDiff = ?E1-?S1

Scramble(CStart,CDiff)

;Here's the protected code!
!_MarkBegin1 db $eb,$06,$eb,$fc,$eb,$fa,$eb,$f8
S1:
MessageRequester("","This code here is scrambled")
For t = 97 To 122
 a$ + Chr(t)
Next
MessageRequester("","Result "+a$)
E1:
!_MarkEnd1 db $eb,$04,$eb,$04,$eb,$fc,$eb,$fc
;The protected code ends here

MessageRequester("","OUT MWAHAHAHAHAHAHAHAHAHAHA")
Enjoy!
None are more hopelessly enslaved than those who falsely believe they are free. (Goethe)
User avatar
DoubleDutch
Addict
Addict
Posts: 3220
Joined: Thu Aug 07, 2003 7:01 pm
Location: United Kingdom
Contact:

Post by DoubleDutch »

Why do you have to implement a complex search when the whole search mask will fit in a quad?

That's why I made it 8 bytes long. :)

edit: I like the newer xor method though.
Last edited by DoubleDutch on Tue Oct 16, 2007 3:25 pm, edited 1 time in total.
https://deluxepixel.com <- My Business website
https://reportcomplete.com <- School end of term reports system
Inf0Byt3
PureBasic Fanatic
PureBasic Fanatic
Posts: 2236
Joined: Fri Dec 09, 2005 12:15 pm
Location: Elbonia

Post by Inf0Byt3 »

Yup, you are right, but the whole search/replace stuff was made before you added your corrected version :).
None are more hopelessly enslaved than those who falsely believe they are free. (Goethe)
User avatar
DoubleDutch
Addict
Addict
Posts: 3220
Joined: Thu Aug 07, 2003 7:01 pm
Location: United Kingdom
Contact:

Post by DoubleDutch »

If you use the quad search from bit I posted then the code will get a lot shorter. :)
https://deluxepixel.com <- My Business website
https://reportcomplete.com <- School end of term reports system
PeterH
User
User
Posts: 28
Joined: Sun Apr 11, 2010 11:01 am

Re: Encrypt code in exes (decrypt at runtime)

Post by PeterH »

Excuse me for bumping.

For one, are there any verifications that the above code (except for the xor-encryption) still works? It seems interesting.
Using the correction I've posted in http://www.purebasic.fr/english/viewtop ... 62#p321431 this could be a pretty nice way to scare off the little amateur with a hex editor. Combined with other methods it could get pretty good.

The corrected xor-encryption routine:

UPDATED CODE (Fred reminded me of register preservation):

Code: Select all

Procedure XorWithKey (sText.l, LenText.l, sKey.l, LenKey.l)
  PUSHA
  MOV ebx, LenKey
  MOV ecx, LenText
  DEC ecx
  MOV edi, sKey
  MOV esi, sText
 xornextbyte:
  MOV eax, ecx
  CDQ
  DIV ebx
  MOV al, [edx + edi]
  XOR [ecx + esi], al
  DEC ecx
  JNS l_xornextbyte
  POPA
EndProcedure
sput
New User
New User
Posts: 2
Joined: Fri Jan 14, 2011 3:16 pm

Re: Encrypt code in exes (decrypt at runtime)

Post by sput »

Doesn't work with PB 4.51

Can you post a working version ?

Thanks
User avatar
ultralazor
Enthusiast
Enthusiast
Posts: 186
Joined: Sun Jun 27, 2010 9:00 am

Re: Encrypt code in exes (decrypt at runtime)

Post by ultralazor »

A cracker will just use the stub to decrypt the blocks(which will stand out in most tools) and dump it.

If you want to do this proper just get PE pointer from 0x3C, then get entry point which is usually 4 bytes from that section, and insert stub there, and decrypt the entire code section. You can actually decrypt all sections from there, just make sure to change size offsets in header if you make them bigger, and make sure encrypted sections are writable.

How to improve it:
-RC5 encrypt resource section and realign if needed, also rename it
-VM with handler stub at OEP that decrypts VM entry points on the fly and creates timer threads on calls
-Have ring3 anti debugs inside VM
-VM handles all JMP, and CALL instructions
-use enduser labels for VM where blocks first goes through RC5 decryption then the VM which emulates JMP,CALL,CMP and more
-use many polymorphic functions
-insert garbage in VM blocks, code section(s) and a little in heaps
-implement some heap checksums

Also allow another label to insert some form of trigger. An make a VM scheme like a jump decrypts next one and encrypts self, and vm handles decrypted same way. Also some server auther where there is a keygen to decrypt the first VM handler or prompt and exit.

Armadillo is a joke BTW, they embed a DLL that actually helps the cracker bypass license. There is actually an up-to-date tool that does it for you. TheMida and SecurEngine and maybe VMProtect are the only ones that are remotely hard, and scripts exist for rebuilding latest versions with all options on..Securom and Starforce are just vm protectors with disc based crypt used on the VM.

You have to actually rebuild a PE protected with VM, crypter stubs you just run and dump. Skilled reversers will just publish scripts for rebuilding either way...this only hurts independent devs IMO cause it keeps the poor poor and the rich competing with the poor..economics

EDIT:All you have to do to defeat the latest code here is BP on unrecognized code in code section, step over it, then patch if needed and dump. A noob could do it in minutes with ollydbg. Also jump the stub, or wipe it and realign. Or even better inline patch it at runtime to use your own key :p

EDIT2:If anyone is interested in PB code that will VM+encrypt protect any EXE/DLL/SYS/SCR without having to write labels in the target code, then you can contact me; a solution for Linux and Mac ELF is also available with same features. I'm not interested in publishing one cause people don't want to pay license fees that cover the time developing it, and act like you lied when someone defeats it...
so many ideas so little time..
User avatar
4RESTER
User
User
Posts: 63
Joined: Thu Aug 19, 2010 11:03 pm
Location: Uman, Ukraine

Re:

Post by 4RESTER »

Rook Zimbabwe wrote:Not to flog a dead pony or anything, BUT!!!

With some slight revision of this you could create a Armadillo like Software security system.

:D
Armadillo is TOO EASY TO GET OFF :lol:

(I can break TEA/XTEA/XXTEA at seconds on the hardware "mate" somelike to "copacobana").
See ya... Image
User avatar
ultralazor
Enthusiast
Enthusiast
Posts: 186
Joined: Sun Jun 27, 2010 9:00 am

Re: Re:

Post by ultralazor »

4RESTER wrote:
Rook Zimbabwe wrote:Not to flog a dead pony or anything, BUT!!!

With some slight revision of this you could create a Armadillo like Software security system.

:D
Armadillo is TOO EASY TO GET OFF :lol:

(I can break TEA/XTEA/XXTEA at seconds on the hardware "mate" somelike to "copacobana").
why keygen and do crypto when you can just do a loader, or inline their DLL and rebuild? The DLL method bypasses nano and everything else..the loader methods works on the latest still and is noob-easy xD

If a protector isn't at least VM based with it's engine and keys encrypted using non-standard routines, then there are going to be very few people who can't defeat it with minimal effort and time..shareware level warez teams currently do any protector including dongle based inside a day, usually an hour with ones most 'skilled' people can't.

Only exception is some dongle ones that do isolated crypto properly, but these are rarely used..

methods shown here defeat themselves..breakpoint..jmp..done. PB compacts code around syscalls, making RCE even easier too.
so many ideas so little time..
MisterDr
User
User
Posts: 41
Joined: Mon Sep 29, 2003 4:14 pm

Re: Encrypt code in exes (decrypt at runtime)

Post by MisterDr »

It is almost useless because you cannot encrypt functions.
Reason for that is because Fred is using macros to assemble ASM file ( macro MP6{
_Procedure6: ......... )

and after that he is calling macros at the end of ASM file so your code will end up like this:

Code: Select all

; !_MarkEnd1 db $ea,$04,$ea,$04,$ea,$fc,$ea,$fc
_MarkEnd1 db $ea,$04,$ea,$04,$ea,$fc,$ea,$fc
; 
_PB_EOP_NoValue:
  PUSH   dword 0
_PB_EOP:
  CALL  _PB_EndFunctions
  PUSH   dword [PB_MemoryBase]
  CALL  _HeapDestroy@4
  CALL  _ExitProcess@4
_PB_EndFunctions:
  CALL  _PB_FreeMemorys@0
  CALL  _PB_FreeLibraries@0
  CALL  _PB_FreeGadgets@0
  CALL  _PB_FreeWindows@0
  CALL  _PB_FreeImages@0
  CALL  _PB_EndAlphaImage@0
  RET
; 
MP4
MP72
MP14
MP16
MP18
MP50
MP52
MP56
MP58
MP8
MP78
MP80
MP82
MP86
MP102
MP88
MP104
MP70
MP106
MP90
MP108
MP92
MP94
MP112
MP54
MP64
MP74
MP110
MP76
MP84
MP100
MP96
MP98
MP68
MP128
MP130
MP134
MP132
MP62
MP60
MP116
MP124
MP122
MP120
MP126
MP114
MP118
MP66
MP136
MP146
MP154
MP138
MP162
MP2
MP164
MP0
And to achieve full encryption you need to put encryption routine in all procedures, also .data section is not encrypted so your key can be easily viewed from hex edit.
Thorium
Addict
Addict
Posts: 1305
Joined: Sat Aug 15, 2009 6:59 pm

Re: Encrypt code in exes (decrypt at runtime)

Post by Thorium »

MisterDr wrote: And to achieve full encryption you need to put encryption routine in all procedures, also .data section is not encrypted so your key can be easily viewed from hex edit.
I wouldnt care about the key at all. Just wait for the procedure to decrypt itself and then dump it.
Post Reply