How can a .bat file be 'converted' to .exe without third party tools?
Asked Answered
F

5

51

There are many reasons to want to 'convert' a .bat to .exe - to hide/obfuscate implementation, passwords, path to resources , to create a service from batch file ... and mainly to make your work to look more complicated and important than it really is.

There are also many reasons to not want to use third party tools.

So what if you want to 'convert' a batch file to .exe without external software? (convert is in quotes because I don't think there's really way to compile a batch file to executable. There are too many abusive twisty techniques and bugs used extensively and all the tools that I know in fact create a temporary .bat file and then call it )

Frowzy answered 27/1, 2015 at 15:45 Comment(9)
An .exe can be hacked. Not a secure method to hide a password. And I don't agree an .exe makes my work look more important or complicated.Homogeny
@Blam - everything can be hacked.This is just a little obfuscation than anything else.Frowzy
@Frowzy You've been busy! :) I love self-question/answer posts. Whoever voted to close this needs to retract his vote.Publicness
Then may I suggest you edit the question to state 'obfuscate' rather than 'hide'. To me the only time I would use this is if I don't want someone mucking with my .bat. It does not hide, secure, or make me look more important. You have an interesting answer but I feel the question is a bit misleading.Homogeny
I don't like self-question/answer posts, but this is a pretty good one because it encourages me to post my own answer (instead of explain everything about the topic) +1 :) And I agree with rojo's comment!Agrostology
@Blam - this is exactly the the kind of development I've seen in the past - from a developers coming from a particular densely populated country .A lot of .bat files converted with f2ko and their aim was to present themselves like C/C++ developers :-) .So as I said there are many reasons to want to do this ....Frowzy
@Publicness - I like your's too - #15885632 :-) . I try to present a simple solutions for a common problems but sometimes it's a lot of work .Frowzy
Just because you have seen it does not mean it should be promoted. Reason and valid reason are not the same. It is not my intent to criticize the answer. I just don't agree with the way the question is framed. It was just a comment.Homogeny
@Agrostology , What about Dave's What are the undocumented features and limitations of the Windows FINDSTR command?. Too me that is the BEST self question and answer on StackOverFlow!Preexist
F
68

One very obvious approach is to use IEXPRESS - the ancient built-in tool that creates self-extracting packages and is capable to execute post extraction commands. So here's IEXPRESS sed-directive/.bat file that creates a self-extracting .exe with packed .bat. It accepts two arguments - the .bat file you want to convert and the target executable:

 ;@echo off
; rem https://github.com/npocmaka/batch.scripts/edit/master/hybrids/iexpress/bat2exeIEXP.bat
;if "%~2" equ "" (
; echo usage: %~nx0 batFile.bat target.Exe
;)
;set "target.exe=%__cd__%%~2"
;set "batch_file=%~f1"
;set "bat_name=%~nx1"
;set "bat_dir=%~dp1"

;copy /y "%~f0" "%temp%\2exe.sed" >nul

;(echo()>>"%temp%\2exe.sed"
;(echo(AppLaunched=cmd.exe /c "%bat_name%")>>"%temp%\2exe.sed"
;(echo(TargetName=%target.exe%)>>"%temp%\2exe.sed"
;(echo(FILE0="%bat_name%")>>"%temp%\2exe.sed"
;(echo([SourceFiles])>>"%temp%\2exe.sed"
;(echo(SourceFiles0=%bat_dir%)>>"%temp%\2exe.sed"
;(echo([SourceFiles0])>>"%temp%\2exe.sed"
;(echo(%%FILE0%%=)>>"%temp%\2exe.sed"


;iexpress /n /q /m %temp%\2exe.sed

;del /q /f "%temp%\2exe.sed"
;exit /b 0

[Version]
Class=IEXPRESS
SEDVersion=3
[Options]
PackagePurpose=InstallApp
ShowInstallProgramWindow=0
HideExtractAnimation=1
UseLongFileName=1
InsideCompressed=0
CAB_FixedSize=0
CAB_ResvCodeSigning=0
RebootMode=N
InstallPrompt=%InstallPrompt%
DisplayLicense=%DisplayLicense%
FinishMessage=%FinishMessage%
TargetName=%TargetName%
FriendlyName=%FriendlyName%
AppLaunched=%AppLaunched%
PostInstallCmd=%PostInstallCmd%
AdminQuietInstCmd=%AdminQuietInstCmd%
UserQuietInstCmd=%UserQuietInstCmd%
SourceFiles=SourceFiles

[Strings]
InstallPrompt=
DisplayLicense=
FinishMessage=
FriendlyName=-
PostInstallCmd=<None>
AdminQuietInstCmd=
UserQuietInstCmd=

example:

bat2exeIEXP.bat  myBatFile.bat MyExecutable.exe

This should work practically on every Windows machine out there but has one major limitation - you cannot pass arguments to the created .exe file

So one other possible approach is to look at the .NET compilers (again should be available on almost every win machine).I've choose Jscript.net . This is a hybrid jscript.net/.bat script that will read the .batch file content.Will create another jscript.net with the .bat file content and after the compilation will create a new bat file int the temp folder and will call it.And will accept command line arguments.(explained might look complex but in fact it's simple):

@if (@X)==(@Y) @end /* JScript comment
@echo off
setlocal

del %~n0.exe /q /s >nul 2>nul

for /f "tokens=* delims=" %%v in ('dir /b /s /a:-d  /o:-n "%SystemRoot%\Microsoft.NET\Framework\*jsc.exe"') do (
   set "jsc=%%v"
)

if not exist "%~n0.exe" (
    "%jsc%" /nologo /out:"%~n0.exe" "%~dpsfnx0"
)

%~n0.exe  "%jsc%" %*
del /q /f %~n0.exe 1>nul 2>nul 
endlocal & exit /b %errorlevel%
*/

//https://github.com/npocmaka/batch.scripts/blob/master/hybrids/.net/bat2exe.bat
import System;
import System;
import System.IO;
import  System.Diagnostics;


var arguments:String[] = Environment.GetCommandLineArgs();
if (arguments.length<3){
    Console.WriteLine("Path to cmd\bat file not given");
    Environment.Exit(1);
}

var binName=Path.GetFileName(arguments[2])+".exe";
if(arguments.length>3){
    binName=Path.GetFileName(arguments[3]);
}
var batchContent:byte[]= File.ReadAllBytes(arguments[2]);
var compilerLoc=arguments[1];

var content="["

for (var i=0;i<batchContent.length-1;i++){
    content=content+batchContent[i]+","
}
content=content+batchContent[batchContent.length-1]+"]";
var temp=Path.GetTempPath();
var dt=(new Date()).getTime();
var tempJS=temp+"\\2exe"+dt+".js";


var toCompile="\r\n\
import System;\r\n\
import System.IO;\r\n\
import  System.Diagnostics;\r\n\
var batCommandLine:String='';\r\n\
//Remove the executable name from the command line\r\n\
try{\r\n\
var arguments:String[] = Environment.GetCommandLineArgs();\r\n\
batCommandLine=Environment.CommandLine.substring(arguments[0].length,Environment.CommandLine.length);\r\n\
}catch(e){}\r\n\
var content2:byte[]="+content+";\r\n\
var dt=(new Date()).getTime();\r\n\
var temp=Path.GetTempPath();\r\n\
var nm=Process.GetCurrentProcess().ProcessName.substring(0,Process.GetCurrentProcess().ProcessName.length-3);\r\n\
var tempBatPath=Path.Combine(temp,nm+dt+'.bat');\r\n\
File.WriteAllBytes(tempBatPath,content2);\r\n\
var pr=System.Diagnostics.Process.Start('cmd.exe','/c '+' '+tempBatPath+' '+batCommandLine);\r\n\
pr.WaitForExit();\r\n\
File.Delete(tempBatPath);\r\n\
";

File.WriteAllText(tempJS,toCompile);
var pr=System.Diagnostics.Process.Start(compilerLoc,'/nologo /out:"'+binName+'" "'+tempJS+'"');
pr.WaitForExit();
File.Delete(tempJS);

It's rather a POC , but .NET System.Diagnostics and System.IO libraries are powerful enough to add features like hidden start , enctiption and etc.You can check also jsc.exe compiling options to see what else is capable of (like adding resources).

I promise an upvote to every improvement over the .NET method :-)

UPDATE: the second script has been changed and now the exe from the converted bat file can be started with double click.It uses the same interface as previous script:

bat2exejs.bat example.bat example.exe
Frowzy answered 27/1, 2015 at 15:46 Comment(5)
How to handle relative paths in this case?Mediocre
@Mediocre - can you give more details with your issue. The iexpress script converts the path to fully qualified path with ;set "batch_file=%~f1" - if you need something more specific you can call it from another script that tunes your pathsFrowzy
The first script did not work for me until I added ";" at the end.Artemisa
This scripts generates a .cab file for me and not .exeChorea
@Chorea - what version of windows you are using? Have pointed the extension in the command line? It creates a self-extracting archive with "post command" that calls the script and the icon that uses comes from windows.Frowzy
M
5

I do know how to convert bat/cmd to exe manually, make sure the bat/cmd filename contains just letters, and numbers. Open 'IExpress Wizard' as admin.

  1. Select 'Create new Self Extraction Directive file'
  2. Select 'Extract files and run an installation command'
  3. Name the package anything
  4. 'No prompt' for 'Confirmation prompt'
  5. 'Do not display a license' for 'License agreement'
  6. Click 'Add' for the 'Packaged files', from there select the bat/cmd file
  7. Then in 'Install Program' text box for 'Install Program to Launch', type cmd /c, followed by the full name of the bat/cmd file, (example: emptyrecyclebin.bat => cmd /c emptyrecyclebin.bat)
  8. Leave the 'Post Install Command' as is
  9. 'Hidden' for 'Show window'
  10. 'No message' for 'Finished message'
  11. Click 'Browse', and select where to download the exe to
  12. Enable 'Hide File Extracting Progress Animation from User'
  13. Disable 'Store files using Long File Name inside Package'
  14. Definitely 'No restart' for 'Configure restart'
  15. Then save SED if you want to re-compile it quicker later
  16. Then create the package! A command window should quickly appear and disappear
  17. Navigate to the place where you downloaded the exe to, and enjoy!
Maleki answered 10/1, 2019 at 9:33 Comment(0)
C
4

All of the above methods do not protect your source code in any way. I recently saw a press release in the news about a new compiler that does not depend on CMD.exe. I don't know exactly how it works, but it doesn't create temporary files at runtime. https://www.prlog.org/12882479-the-worlds-first-true-compiler-for-batch-files.html

Using IEXPRESS makes a kind of SFX archive, which does not achieve the main goal of hiding the source code with passwords. All other "compilers" work on a similar principle - extract the script to a temporary folder and run it via cmd.exe. The press release claimed a real compilation, so I downloaded the trial version and wrote a primitive script to measure the speed:

@echo off
set startTime=%time%     
set counter=0     
:main_loop
echo %counter%
set /a counter=counter+1
if %counter% LEQ 10000 goto main_loop

echo Start Time: %startTime%
echo Finish Time: %time%

After compiling the code runs twice as fast and I haven't noticed any calls to cmd.exe in the Task Manager. In addition, I ran Process Monitor and saw no creation of temporary files while it was running. This leads me to believe that this compiler provides better protection for the source code.

Cuellar answered 30/8, 2021 at 0:24 Comment(1)
Please add further details to expand on your answer, such as working code or documentation citations.Outward
F
2

You can also develop a simple exe, which just calls your bat-script.

For example you could write one in C# (I'm no C#-Pro, this is actually my first program and I copied lots of it from this other Stackoverflow post.):

using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.IO;

class BatCaller {
    static void Main() {
        var batFile = System.Reflection.Assembly.GetEntryAssembly().Location.Replace(".exe", ".bat");
        if (!File.Exists(batFile)) {
            MessageBox.Show("The launch script could not be found.", "Critical error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            System.Environment.Exit(42);
        }
        var processInfo = new ProcessStartInfo("cmd.exe", "/c \"" + batFile + "\"");
        processInfo.CreateNoWindow = true;
        processInfo.UseShellExecute = false;
        processInfo.RedirectStandardError = true;
        processInfo.RedirectStandardOutput = true;

        var process = Process.Start(processInfo);

        process.OutputDataReceived += (object sender, DataReceivedEventArgs e) => Console.WriteLine("output>>" + e.Data);
        process.BeginOutputReadLine();

        process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) => Console.WriteLine("error>>" + e.Data);
        process.BeginErrorReadLine();

        process.WaitForExit();

        Console.WriteLine("ExitCode: {0}", process.ExitCode);
        process.Close();
    }
}

If you store this code above to MySuperApp.cs just next to MySuperApp.bat and then compile it with csc.exe /target:winexe MySuperApp.cs (and maybe even add /win32icon:MySuperApp.ico to add a fancy icon) it will generate a MySuperApp.exe.

Launching MySuperApp.exe will call MySuperApp.bat (the bat-file with the same name).

csc.exe (should?) be present on every Windows machine.

Finback answered 15/2, 2019 at 9:39 Comment(2)
Does it require .NET Framework to be installed on the target platform?Dogma
No, .NET is only required when building the exe.Finback
B
1

Different versions of Windows has different effects for same batch file commands, and some commands are limited to some Windows systems eg. findstr and shutdown.
BTW, Win 10 CMD doesn't allow changes to SETLOCAL on command line. OK for batch files.

See this link for different commands for restarting different versions of windows: https://www.computerhope.com/issues/ch000321.htm

So if you were to compile a script on Win 98, and run on Win 8.1, you'd get unexpected results or scripts may not even work. See list of commands here: https://www.ionos.com/digitalguide/server/know-how/windows-cmd-commands/

For this reason, one would need a different compiler on each version of Windows, preferably which would spit out binary code (generic) that can be run on as many CPU chips as possible, with same instruction sets. A workaround offered by most programs is to wrap the script in an exe file that would unwrap and execute the script when opened/run eg. Bat_To_Exe_Converter, Bat2Exe, BatchCompiler, iexpress or Winzip: https://support.winzip.com/hc/en-us/articles/115011794948-What-is-a-Self-Extracting-Zip-File-

To solve this issue of portability, virtual machines have become more popular and hence the rise of Java & related scripts.

This however, would still be intepreted code, and not as fast as compiled code. Even byte code (intermediate code) from virtual machines still need to be compiled, even if it's (JIT): https://aboullaite.me/understanding-jit-compiler-just-in-time-compiler/

In short, you can get an exe file which would contain a script that would be intepreted by the command processor, but it won't be a native executable file, meaning it won't run without a host by the Windows operating system.

Beckiebeckley answered 4/4, 2020 at 17:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.