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.NETFramework*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 cmdat 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="
import System;
import System.IO;
import System.Diagnostics;
var batCommandLine:String='';
//Remove the executable name from the command line
try{
var arguments:String[] = Environment.GetCommandLineArgs();
batCommandLine=Environment.CommandLine.substring(arguments[0].length,Environment.CommandLine.length);
}catch(e){}
var content2:byte[]="+content+";
var dt=(new Date()).getTime();
var temp=Path.GetTempPath();
var nm=Process.GetCurrentProcess().ProcessName.substring(0,Process.GetCurrentProcess().ProcessName.length-3);
var tempBatPath=Path.Combine(temp,nm+dt+'.bat');
File.WriteAllBytes(tempBatPath,content2);
var pr=System.Diagnostics.Process.Start('cmd.exe','/c '+' '+tempBatPath+' '+batCommandLine);
pr.WaitForExit();
File.Delete(tempBatPath);
";
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
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…