Two years ago I published my Perl2Exe back to Perl article in Digital Forensics Magazine, more information can be found in my post here. Since I published this article in a magazine I was not allowed to post it on my own website as well, but since enough time has passed I am now allowed to publish the full article which can be found below.
Reverse engineering Perl2Exe back to Perl
Perl2Exe is a program which converts Perl source code to standalone Windows executable files which hide the Perl code. When a forensic investigator encounters a Perl2Exe program (for example malware) it can take a lot of effort to analyse these files. This article describes a new and easy to follow approach to recover the full Perl source code from these Perl2Exe executable files, making the analysis of these files much easier.
The Perl2Exe program is published by IndigoStar which can be found online at http://www.indigostar.com/. The best way to describe Perl2Exe is the following quote from the IndigoStar website:
Perl2Exe is a command line program for converting Perl scripts to executable files.
This allows you to create stand alone programs in Perl that do not require the Perl interpreter. You can ship the executable files without having to ship your Perl source code.
Source: http://www.indigostar.com/perl2exe.php (17-06-2012)
Perl2Exe converts the source code of the Perl script by packing it inside a single executable together with a Perl interpreter. The Perl source code is included inside the executable in encrypted form and thus it cannot easily be recovered from the executable.
In the past there have been a couple of other projects to retrieve the Perl source code from Perl2Exe executable files (see list at the end of this article). However, none of these projects work with the current versions of Perl and Perl2Exe. In the past couple of years there have been a number of Perl2Exe versions released (see list at the end of this article) and each new version seems to break the previously found solutions to recover the Perl source code.
The two approaches to recover the Perl source code described in this article have been tested successfully against Perl2Exe version v8.82 and higher, up till the latest version at this moment (v11.00). More details on the compatibility with the Perl and Perl2Exe versions can be found further on in this article.
The different Perl versions and Perl2Exe program versions have been tested on multiple Microsoft Windows versions including Windows XP (32-bit) and Microsoft Windows 7 (64-bit) virtual machines as well as live machines. The small and simple test Perl code used for this project is shown below:
#!/usr/bin/perl print "\n[*] Perl2exe Perl sourcecode revealer test code\n". " by Thijs (Thice) Bosschert\n". " v1.0 17-06-2012\n\n"; # Comment: This is a test comment print "\n[*] This is just a test line.\n\n";
Creating Perl2Exe executable files is as simple as running the program with the Perl source code file as an argument, for example:
After running the Perl2Exe program an executable is created from the Perl source code, in this example that executable would be Example_code.exe, and the output of this executable is shown below:
[*] Perl2exe Perl sourcecode revealer test code by Thijs (Thice) Bosschert v1.0 17-06-2012 [*] This is just a test line.
How a Perl2Exe generated program works
The newly created Perl2Exe executable contains the encrypted Perl source code as well as a Perl interpreter. To be able to run the Perl script the Perl2Exe executable needs to launch the Perl interpreter first. A simplified schematic overview of the involved steps is shown in figure 1.
Figure 1 – Overview of Perl2Exe components
To launch the Perl interpreter it is unpacked and stored on the local hard drive by the Perl2Exe executable. The file locations where the Perl interpreter file is stored are shown below:
Windows XP / 2000 / 2003:
%HOMEPATH%\Local Settings\Temp\p2xtmp-#### (C:\Documents and Settings\<username>\Local Settings\Temp\p2xtmp-####)
Windows Vista / 7:
In both file locations the #### part of the directory name is the process ID number the program is running under, which changes each time the Perl2Exe executable is executed. As soon as the Perl2Exe executable finishes running, the temporary directory will be removed again from the system.
The file that the Perl2Exe executable will write to the temporary directory is named p2x####.dll, the #### part of the filename consists out of three or four digits which refer to the Perl version the Perl interpreter uses. For example the file p2x5142.dll is the Perl interpreter for Perl version 5.14.2 and the file p2x588.dll is the Perl interpreter for Perl version 5.8.8.
The Perl interpreter DLLs written to the temporary directory are packed with the UPX packer, however they can easily be unpacked with UPX as well as can be seen below:
C:\UPX\upx307w>upx.exe -d -o c:\p2x5142_u.dll c:\p2x5142.dll Ultimate Packer for eXecutables Copyright (C) 1996 - 2010 UPX 3.07w Markus Oberhumer, Laszlo Molnar & John Reiser Sep 08th 2010 File size Ratio Format Name -------------------- ------ ----------- ----------- 937984 <- 437248 46.62% win32/pe p2x5142_u.dll Unpacked 1 file.
After unpacking the DLL file it can be easier analysed with tools such as IDA (http://www.hex-rays.com/products/ida/index.shtml). However that is not the approach we will follow in this article because these DLL files only contain the Perl interpreter and in this case we are interested in the Perl source code.
After storing the Perl interpreter DLL file in the temporary directory the Perl2Exe executable will load it so it can run the Perl script. The Perl script is included in the Perl2Exe executable, but not within the Perl interpreter, the Perl source code is only loaded in to the memory and not written to the local hard drive.
Recovering the Perl script source code
Since the Perl script source code is not written to the hard drive we need to find a way to recover it in a different way. Luckily the Perl interpreter contains code to store certain kinds of other files to the local hard drive in the same temporary directory as the Perl interpreter is stored in. We are going to use this feature to our own benefit and let Perl2Exe do the hard work for us. We can adjust the program code of the Perl interpreter with some small changes so that it will also write the Perl source code to the temporary directory. The Perl interpreter checks which kind of files it reads from memory and if these kind of files need to be stored in the temporary directory. We will adjust the flow of this piece of program code so that it will also write the Perl source code to the temporary directory. A very basic schematic overview of the theory behind the changes to the Perl interpreter is shown in figures 2 and 3.
The original flow of a small part of the Perl interpreter:
Figure 2 – Original flow of a small part of the Perl interpreter
The adjusted flow of a small part of the Perl interpreter:
Figure 3 – adjusted flow of a small part of the Perl interpreter
In the schematics above the Perl script source code would originally not be written to the temporary directory, but after changing the flow of the program it will be.
The adjustments we are going to make to the code are only meant to retrieve the Perl script source code and not to have the program continue in a normal way. Since we adjust some of the program code it can easily become unstable or give an error later on. However, since we are making changes to a temporary file the changes will be gone as soon as the program ends and the temporary directory is removed again. Note: the approach described in this article executes the Perl program, which in case the executable is malware means that this malware is executed on the local system. Be careful and always analyse unknown files in a controlled lab environment such as a Virtual Machine.
To make the proposed adjustment to the program code of the Perl interpreter we will make use of the debugger OllyDbg (http://www.ollydbg.de/). The first step is to load the Perl2Exe executable in OllyDbg, to do so we start OllyDbg and then use the following menu option to load the Perl2Exe executable file:
File → Open → Choose the Perl2Exe executable you want to analyse
After the file is loaded we will scroll to the top of the page and then scroll down till we find the string RunPerl, after clicking on this line we will press F2 to set a breakpoint, after doing so the address in front of this line will turn red as can be seen in the screenshot in figure 4.
Figure 4 – OllyDbg screenshot of RunPerl location
The breakpoint we have just set will pause the execution of the Perl2Exe executable at the moment it already has extracted the Perl interpreter to the temporary directory but before this interpreter is started. This allows us to make the adjustment to the Perl interpreter as described earlier. To start the program we are analysing we need to press the play button in the shortcut bar or press the F9 button, both will start the program which will then run and pause at our newly created breakpoint.
After we hit our breakpoint it is time to switch from the Perl2Exe executable to the Perl interpreter. We can easily do this switch within OllyDbg by using the following steps. We need to launch the Executable modules window which we can do by clicking on the light blue square with the E in it on the shortcut bar or by pressing the key combination Alt+E. Within the newly opened window we will see the .exe and .dll files which are loaded by the running program. We will quickly notice that one of these files is the Perl interpreter DLL file which is stored in the temporary directory. This is the file we want to open and we can do this by double clicking it. As soon as this is done we will be looking at the assembly code of the Perl interpreter.
Finding the specific location of the code we want to change can be quite a lot of work, but since this work already has been done by me we will use a simple trick to find this location instead. The trick we will use is to search for a specific unique string which will bring us right to the location where we need to make a small adjustment.
To be able to search for strings in OllyDbg we need to bring up the Text strings window which can easily be done by clicking the right mouse button somewhere inside the main OllyDbg window and select Search for and then select All referenced strings. The screenshot of this action is shown in figure 5.
Figure 5 – Overview of ‘All referenced strings’ command location
The window that is opened shows a lot of text strings in the Perl interpreter file, don’t be alarmed by this, we are only interested in a specific string which we will search for. To open the search window we need to click the right mouse button and select Search for text or press the key combination Ctrl+F.
The string we are going to look for and the code we are going to adjust is different for the Perl2Exe versions 8/9 and 10/11. We will first discuss versions 10 and 11 and then show the difference for versions 8 and 9.
Perl2Exe versions 10 and 11
When analyzing a Perl2Exe executable which is created with Perl2Exe version 10 or 11 we will use the following steps. In the search window we will search for the string P2XDLL, if the string is not found it is quite likely that the Perl2Exe version used by the executable is version 9 or older and the approach for those versions should be followed. If the string is found the Perl2Exe version is version 10 or higher and when double clicking the P2XDLL string it will return us to the Perl interpreter program code at the location that we are interested in. The part of the code we are interested in is shown in the overview below.
Address Hex dump Command Comments 280B29B3 68 9CF80C28 PUSH 280CF89C ; ASCII "P2XDLL/" 280B29B8 56 PUSH ESI 280B29B9 FFD7 CALL EDI 280B29BB 59 POP ECX 280B29BC 3BC6 CMP EAX,ESI 280B29BE 59 POP ECX 280B29BF 0F84 05010000 JE 280B2ACA 280B29C5 68 94F80C28 PUSH 280CF894 ; ASCII "DLL/" 280B29CA 56 PUSH ESI 280B29CB FFD7 CALL EDI 280B29CD 59 POP ECX 280B29CE 3BC6 CMP EAX,ESI 280B29D0 59 POP ECX >>280B29D1 75 29 JNE SHORT 280B29FC 280B29D3 68 94F80C28 PUSH 280CF894 ; ASCII "DLL/"
The part in the code overview that we want to change is pointed out by the two arrows in front of it, it is the last line before the second line with ASCII “DLL/”. The line contains a jump instruction and this specific jump (JNE, Jump Not Equal) is the location we can change to make sure the Perl source code is going to be saved in the temporary directory. We will remove this jump completely from the code by changing it to NOP (No Operation) instructions, which will do nothing and make sure the code continues after it. To make this change will just need to double click the line with the JNE in it and then change the text in the small pop-up window to NOP, after clicking Assemble we can Close the pop-up. The resulting code after doing this is shown below.
Address Hex dump Command Comments 280B29B3 68 9CF80C28 PUSH 280CF89C ; ASCII "P2XDLL/" 280B29B8 56 PUSH ESI 280B29B9 FFD7 CALL EDI 280B29BB 59 POP ECX 280B29BC 3BC6 CMP EAX,ESI 280B29BE 59 POP ECX 280B29BF 0F84 05010000 JE 280B2ACA 280B29C5 68 94F80C28 PUSH 280CF894 ; ASCII "DLL/" 280B29CA 56 PUSH ESI 280B29CB FFD7 CALL EDI 280B29CD 59 POP ECX 280B29CE 3BC6 CMP EAX,ESI 280B29D0 59 POP ECX >>280B29D1 90 NOP >>280B29D2 90 NOP 280B29D3 68 94F80C28 PUSH 280CF894 ; ASCII "DLL/"
At this moment the program will write all the files it comes across to the temporary directory including the Perl source code we are after. However, after the program finishes the whole temporary directory will be removed again from the system which means we lose all the files again including the Perl source code. To avoid this we will need to set a breakpoint in the program to make sure we can recover the files before the program finishes. We will set the breakpoint in the program right after the creation of the temporary files, the location to do this is shown in the code overview below. To set the breakpoint we need to select this line (the one with ADD ESP, 18) and press the F2 button.
Address Hex dump Command Comments >>280B29D1 90 NOP >>280B29D2 90 NOP 280B29D3 68 94F80C28 PUSH 280CF894 ; ASCII "DLL/" 280B29D8 E8 D9520000 CALL 280B29DD FF75 EC PUSH DWORD PTR SS:[EBP-14] 280B29E0 03F0 ADD ESI,EAX 280B29E2 FF75 F0 PUSH DWORD PTR SS:[EBP-10] 280B29E5 FF75 F4 PUSH DWORD PTR SS:[EBP-0C] 280B29E8 FF35 04DE0D28 PUSH DWORD PTR DS:[280DDE04] 280B29EE 56 PUSH ESI 280B29EF E8 73FAFFFF CALL 280B2467 >>280B29F4 83C4 18 ADD ESP,18 280B29F7 E9 CE000000 JMP 280B2ACA
With this breakpoint set we are now ready to start dumping our files to the temporary directory. To start doing this we will need to continue the program which we still got on pause, this can be done by pressing F9 (or using the play button in the shortcut bar). Because of our breakpoint the program will pause after each file creation, but every time we press the F9 button a new file will be created in the temporary directory. After a couple of F9s we will see that a file with the name n.pl is created, which is the main.pl file which contains the full Perl source code that we are after. Make sure you copy this file from the temporary directory to another location because when the program finishes this directory will be removed again.
Now you got the full Perl source code which should make the analysis of this executable file a lot easier and you probably saved you a lot of time with this quick reverse engineering trick.
Perl2Exe versions 8 and 9
When the P2XDLL string from the previous approach cannot be found it is quite likely that the executable was created with an older Perl2Exe version, for example version 8 or 9. In that case we need to follow a couple of different steps to recover the Perl source code. In the search window we will search for the string .pll which we will then double click to return to the program code of the Perl interpreter, this will land us close to the line we need to adjust. The part of the code we are interested in is shown in the overview below, the line we need to adjust is pointed out with the arrows in front of it.
Address Hex dump Command Comments 28091A97 68 8C4B0A28 PUSH 280A4B8C ; ASCII "p2x" 28091A9C 56 PUSH ESI 28091A9D FFD7 CALL EDI 28091A9F 59 POP ECX 28091AA0 3BC6 CMP EAX,ESI 28091AA2 59 POP ECX >>28091AA3 75 1E JNE SHORT 28091AC3 28091AA5 56 PUSH ESI 28091AA6 E8 674F0000 CALL 28091AAB C70424 844B0A28 MOV DWORD PTR SS ; ASCII ".dll" 28091AB2 56 PUSH ESI 28091AB3 8D5C30 FC LEA EBX,[ESI+EAX-4] 28091AB7 FFD7 CALL EDI 28091AB9 59 POP ECX 28091ABA 3BC3 CMP EAX,EBX 28091ABC 59 POP ECX 28091ABD 0F84 BA010000 JE 28091C7D 28091AC3 68 844B0A28 PUSH 280A4B84 ; ASCII ".dll"
Because of the flow of the program of this Perl2Exe version we cannot simply remove this jump instruction but we need to change it so that the jump is going to a different destination. The jump destination that we need can be found in the last jump instruction (JNE) before the line containing the ASCII “.pll” string we saw before. The exact location can be found in the program code below.
Address Hex dump Command Comments 28091AC3 68 844B0A28 PUSH 280A4B84 ; ASCII ".dll" 28091AC8 56 PUSH ESI 28091AC9 FFD7 CALL EDI 28091ACB 59 POP ECX 28091ACC 85C0 TEST EAX,EAX 28091ACE 59 POP ECX >>28091ACF 0F85 09010000 JNE 28091BDE 28091AD5 68 7C4B0A28 PUSH 280A4B7C ; ASCII ".pll"
To change the previous jump to this new destination, we need to double click the JNE jump instruction beneath the ASCII “p2x” string and adjust the jump instruction so that it points to the new location we just identified (the one above theASCII “.pll”line). We will change the jump from a JNE (Jump Not Equal) to a normal JMP (Jump) so that all the files will be dropped by the program including the Perl source code that we are after. Because the assembly instruction for this jump is a different size as the instruction we are changing we need to make sure that the option Keep size in OllyDbg is not selected as can be seen in figure 6.
Figure 6 – OllyDbg jump adjustment screen
After changing the jump we need to set a breakpoint to make sure our dumped Perl source code does not get removed when the whole temporary directory gets removed by the program. The breakpoint for this version is quite simple since it will suffice to put the breakpoint on the instruction we just changed (press F2 to set this breakpoint). The changed code and breakpoint location are shown in the code overview below.
Address Hex dump Command Comments 28091A97 68 8C4B0A28 PUSH 280A4B8C ; ASCII "p2x" 28091A9C 56 PUSH ESI 28091A9D FFD7 CALL EDI 28091A9F 59 POP ECX 28091AA0 3BC6 CMP EAX,ESI 28091AA2 59 POP ECX >>28091AA3 E9 36010000 JMP 28091BDE 28091AA8 90 NOP 28091AA9 90 NOP 28091AAA 90 NOP 28091AAB C70424 844B0A28 MOV DWORD PTR SS ; ASCII ".dll"
After continuing the program (press F9) the program will start writing the different files to the temporary directory and return to the breakpoint every time it has done so. After a couple of F9 presses the file _main.pl will appear which will contain the full Perl source code that we are after. Once again, make sure to copy this file to a different directory so that you do not lose it when the temporary directory is removed.
With a little bit of hands-on reverse engineering we were able to recover the full Perl source code out of a Perl2Exe executable. This Perl source code will make it a lot easier to analyze the file. What is even better is that the Perl source code also includes any comments that might have been included by the author. Before having to use this approach in a running investigation it might make sense to practice a bit in performing the different steps. Executable files which can be used for this can be found on the website in the links section, all of these executable files will be harmless for the system they are used on.
Online test files for different Perl2Exe versions can be found at:
Links to software mentioned in this article:
- Perl2Exe: http://www.indigostar.com/perl2exe.php
- Strawberry Perl: http://strawberryperl.com/
- IndigoPerl: http://www.indigostar.com/indigoperl.php
- ActivePerl: http://www.activestate.com/activeperl
- OllyDbg: http://www.ollydbg.de/ (version used in this article: 2.01)
The following projects have been published in the past with the same goals as this article. However none of them work with the current Perl and Perl2Exe versions:
- The exe2perl.c program created by GalaxyMaster which can be found on http://exe2perl.danuk.ru/
- The exec2pl program from 2002 by Chetan Ganatra, more info can be found on http://www.derkeiler.com/Mailing-Lists/Securiteam/2002-01/0105.html
- The write up by ‘fileoffset’ which describes a similar approach as described in this article and can be found on http://www.fileoffset.com/re/tutorials/perl2exe.htm
The approaches described in this article have been successfully tested against multiple different Perl2Exe versions, Perl versions and platforms including:
- Windows 2000, 2003, XP, Vista and 7 (32-bit and 64-bit)
- Perl2Exe versions 8, 9, 10 and 11
- Strawberry Perl versions v22.214.171.124 till v5.14.2
- ActivePerl version 126.96.36.1995
- IndigoPerl version V9.02
The following Perl2Exe versions have been released over the years:
- Perl2Exe V11.00, released Mar 10, 2012
- Perl2Exe V10.40, released Jun 25, 2011
- Perl2Exe V10.10, released Feb 3, 2011
- Perl2Exe V9.110, released Dec 7, 2009
- Perl2Exe V8.82, release Aug 21, 2007