4

In short I want to give my friend a bat file which does the below (untested)

echo Your mother is so fat
pause
echo the recursive function computing her mass causes a stack overflow

I might copy/paste him the bat file so I don't want the punch line to be ruined. How can I hide the text? I was thinking I can store the string in a variable and before I echo it I should XOR each letter with 32. But I have no idea how to take a string, XOR each letter than echo it to display the joke. How might I hide the text? I could also BASE64 encode/decode it but IDK how to do that either if I am only using a bat file

4 Answers4

9

1) Here's one way to hide the text using mshta as a command line tool (with ascii codes in this case) :

@echo off
mshta vbscript:execute("CreateObject(""Scripting.FileSystemObject"").GetStandardStream(1).Write(Chr(89) & Chr(111)& Chr(117) & Chr(114) & Chr(32) & Chr(109) & Chr(97) & Chr(109) & Chr(97) & Chr(32) ):Close")|more

2) You can use CERTUTIL to encode/decode base64/hex files but it requires a temporary file that can be silently deleted (more info ):

echo 796f7572206d616d6120697320736f20666174>"%temp%\fat.hex"
certutil -decodehex "%temp%\fat.hex" "%temp%\fat.txt" >nul 2>&1
type "%temp%\fat.txt" 
del /q /f "%temp%\fat.txt" 

3) Dbenham's hex print function

@echo off
setlocal

::Define a Linefeed variable
set LF=^


::above 2 blank lines are critical - do not remove.


::Create a string variable with encoded TABs
call :hexprint "0x790x6f0x750x720x200x6d0x610x6d0x610x200x690x730x200x730x6f0x200x660x610x74" var
echo  %var%

exit /b

:hexPrint  string  [rtnVar]
  for /f eol^=^%LF%%LF%^ delims^= %%A in (
    'forfiles /p "%~dp0." /m "%~nx0" /c "cmd /c echo(%~1"'
  ) do if "%~2" neq "" (set %~2=%%A) else echo(%%A
exit /b

4) carlos' genCar function that uses MAKECAB:

@echo off
setlocal


break>fat.txt 
 for %%# in (121 111 117 114 32 109 97 109 97 32 105 115 32 115 111 32 102 97 116) do (
    call :genChar %%#
    type %%#.chr>>fat.txt
    del /q /f %%#.chr >nul 2>&1
 )

type fat.txt 
del /q /f fat.txt 

goto :eof

  :genChar
  setlocal
  set "USAGE=echo:Usage: Supply an integer 0-255& goto :EOF"
  if "%~1" equ "" %USAGE%
  set /a "val=%~1" 2>nul
  if "%~1" neq "%val%" %USAGE%
  if %~1 lss 0    %USAGE%
  if %~1 gtr 255  %USAGE%

  set tempfile=%~1.tmp
  set "options=/d compress=off /d reserveperdatablocksize=26"
  if %~1 neq 26  (type nul >"%tempfile%"
  makecab %options% /d reserveperfoldersize=%~1 "%tempfile%" %~1.chr >nul
  type %~1.chr | (
  (for /l %%N in (1 1 38) do pause)>nul&findstr "^">"%tempfile%")
  >nul copy /y "%tempfile%" /a %~1.chr /b
  del "%tempfile%"
  ) else (copy /y nul + nul /a 26.chr /a >nul)
  endlocal

for more cryptic script you can combine hem.Only MSHTA and MAKECAB solutions will work on every windows machine. FORFILES and CERTUTIL are default form Vista and above I think.It is possible to create a few more examples ...

dbenham
  • 127,446
  • 28
  • 251
  • 390
npocmaka
  • 55,367
  • 18
  • 148
  • 187
  • Wait wait, what limitations does this have? Does it work on windows 8? I couldn't use powershell bc it said the system doesnt have scripts enabled and the idea was not to mess around with settings. Would it work. -edit- I like the certutil solution! –  Aug 30 '14 at 13:14
  • @acidzombie24 you have no limitations with `MSHTA` solution it can be ran on every windows machine out there.`forfiles` and `certutil` are not avaialbe on some home editions of windows.The next example will be with `makecab` also available on every windows machine , but also reqires temp file... – npocmaka Aug 30 '14 at 13:22
  • 1
    +1 I believe CERTUTIL is available on all modern Windows platforms. I edited your post to convert 4. to 4) because of some formatting issues. You might want to look at my answer that uses my Adventure style selective encryption. – dbenham Aug 30 '14 at 15:52
9

This is a subject near and dear to my heart because I did something similar in my implementation of the classic Colossal Cave Adventure game as a Windows batch file.

Within the game script I selectively encrypt display text, variable names, and comments. The code to decode the encrypted text is embedded directly within the same script! I write the source code for the game normally, and use braces to denote what portion is to be encrypted. A function within the game is able to generated the encrypted form of itself!

I used a simple symmetric rotation cipher, so really it is more obfuscation than encryption. But that is all that is needed for both the game, and your situation.

I've extracted a simplified version of the routines and provide them below.

The first script is a standalone script that selectively encrypts text within a source file and writes the result to stdout. Simply redirect the output to a new file to get the encrypted version of the file.

selectiveROT13.bat

@echo off
:selectiveROT13  InFile
::
::  Selectively applies the simple "rotate alphabet 13 places" cipher
::  to the contents of file InFile. Only text between curly braces
::  is affected. The affected content can span multiple lines.
::
::  Writes the results to stdout.
::  Percent completion is continuously written to stderr.
::
setlocal enableDelayedExpansion
set "upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ"
set "lower=abcdefghijklmnopqrstuvwxyz"
for /l %%A in (0 1 25) do (
  set /a "B=(%%A+13)%%26"
  for /f %%B in ("!B!") do (
    set "upper!upper:~%%A,1!=!upper:~%%B,1!"
    set "lower!lower:~%%A,1!=!lower:~%%B,1!"
  )
)
setlocal disableDelayedExpansion
>&2 cls
set "active="
for /f %%N in ('type %1^|find /c /v ""') do set /a "lnCnt=%%N, pct=-1"
for /f "skip=2 tokens=1,* delims=[]" %%a in ('find /v /n "" %1') do (
  set "ln=%%b"
  setlocal enableDelayedExpansion
  set "str=A!ln!"
  set "len=0"
  for /L %%A in (12,-1,0) do (
    set /a "len|=1<<%%A"
    for %%B in (!len!) do if "!str:~%%B,1!"=="" set /a "len&=~1<<%%A"
  )
  set /a len-=1
  set rtn=
  for /l %%n in (0,1,!len!) do (
    set "c=!ln:~%%n,1!"
    if "!c!" equ "{" set "active=1"
    if "!c!" equ "}" set "active="
    if defined active if defined upper!c! for /f %%c in ("!c!") do (
      if "!upper:%%c=%%c!" equ "!upper!" (
        set "c=!upper%%c!"
      ) else (
        set "c=!lower%%c!"
      )
    )
    set "rtn=!rtn!!c!"
  )
  echo(!rtn!
  for %%A in ("!active!") do (
    endlocal
    set "active=%%~A"
  )
)
exit /b 0

Below is your joke program with a simplified version of the code to decode encrypted text. My original code worked with string variables, but this version works with string literals. The source script is written normally, without encryption. Braces indicate which code is to be encrypted. Besides your joke, I've included documentation and examples to demonstrate some of the features.

joke_src.bat

@echo off
setlocal enableDelayedExpansion
call :init

:: Disable delayed expansion to protect ! within string literals
setlocal disableDelayedExpansion

:: Curly braces are used to denote text that should be encrypted.
:: Encryption can span multiple lines
:: {
:::Line1
:::Line2
:::Line3
:: }

:: I defined a simple SHOW macro that expands to CALL :SHOW
:: Use the %show% macro to display encrypted text.
:: The braces can be hidden by using the undefined %{% & %}% variables
%show% %{%"Quote literals ("") must be doubled ("""") in the source"%}%

:: Here I use a FOR loop to show all encrypted lines within this script
:: that begin with :::
echo(
for /f "delims=: tokens=*" %%A in ('findstr /b ":::" "%~f0"') do %show% "%%A"

echo(
echo And now it is time for a little joke.
echo(
echo Your mother is so fat...
pause
%show% %{%"the recursive function computing her mass causes a stack overflow!"%}%
exit /b


:show  Str
::{
::  Applies the simple "rotate alphabet 13 places" cipher to string Str
::  and writes the result to stdout. Consecutive quotes ("") are converted
::  into a single quote (").
::}
  setlocal disableDelayedExpansion
  set "str=%~1"
  setlocal enableDelayedExpansion
  set "str=!str:""="!^"
  if defined {obfuscated} (
    set "len=0"
    set "str2=.!str!"
    for /L %%A in (12,-1,0) do (
      set /a "len|=1<<%%A"
      for %%B in (!len!) do if "!str2:~%%B,1!"=="" set /a "len&=~1<<%%A"
    )
    set /a len-=1
    set rtn=
    for /l %%n in (0,1,!len!) do (
      set "c=!str:~%%n,1!"
      if defined {upper}!c! for /f %%c in ("!c!") do (
        if "!{upper}:%%c=%%c!" equ "!{upper}!" (
          set "c=!{upper}%%c!"
        ) else (
          set "c=!{lower}%%c!"
        )
      )
      set "rtn=!rtn!!c!"
    )
  ) else set "rtn=!str!"
  echo(!rtn!
exit /b 0


:init
  set "}="
  set "{="}
  set "{upper}=ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  set "{lower}=abcdefghijklmnopqrstuvwxyz"
  for /l %%A in (0 1 25) do (
    set /a "B=(%%A+13)%%26"
    for /f %%B in ("!B!") do (
      set "{upper}!{upper}:~%%A,1!=!{upper}:~%%B,1!"
      set "{lower}!{lower}:~%%A,1!=!{lower}:~%%B,1!"
    )
  )
  set "{obfuscated}="
  set "{obfuscationTest}={A}"
  if "!{obfuscationTest}:A=!" equ "!{obfuscationTest}!" set {obfuscated}=1
  set "show=call :show"
exit /b

The following command will generate the encrypted version of the script:

selectiveROT13 joke_src.bat >joke.bat

Below is the encrypted form. This is what you would send to your friend. (Without the extra documentation and examples of course)

joke.bat

@echo off
setlocal enableDelayedExpansion
call :init

:: Disable delayed expansion to protect ! within string literals
setlocal disableDelayedExpansion

:: Curly braces are used to denote text that should be encrypted.
:: Encryption can span multiple lines
:: {
:::Yvar1
:::Yvar2
:::Yvar3
:: }

:: I defined a simple SHOW macro that expands to CALL :SHOW
:: Use the %show% macro to display encrypted text.
:: The braces can be hidden by using the undefined %{% & %}% variables
%show% %{%"Dhbgr yvgrenyf ("") zhfg or qbhoyrq ("""") va gur fbhepr"%}%

:: Here I use a FOR loop to show all encrypted lines within this script
:: that begin with :::
echo(
for /f "delims=: tokens=*" %%A in ('findstr /b ":::" "%~f0"') do %show% "%%A"

echo(
echo And now it is time for a little joke.
echo(
echo Your mother is so fat...
pause
%show% %{%"gur erphefvir shapgvba pbzchgvat ure znff pnhfrf n fgnpx biresybj!"%}%
exit /b


:show  Str
::{
::  Nccyvrf gur fvzcyr "ebgngr nycunorg 13 cynprf" pvcure gb fgevat Fge
::  naq jevgrf gur erfhyg gb fgqbhg. Pbafrphgvir dhbgrf ("") ner pbairegrq
::  vagb n fvatyr dhbgr (").
::}
  setlocal disableDelayedExpansion
  set "str=%~1"
  setlocal enableDelayedExpansion
  set "str=!str:""="!^"
  if defined {boshfpngrq} (
    set "len=0"
    set "str2=.!str!"
    for /L %%A in (12,-1,0) do (
      set /a "len|=1<<%%A"
      for %%B in (!len!) do if "!str2:~%%B,1!"=="" set /a "len&=~1<<%%A"
    )
    set /a len-=1
    set rtn=
    for /l %%n in (0,1,!len!) do (
      set "c=!str:~%%n,1!"
      if defined {hccre}!c! for /f %%c in ("!c!") do (
        if "!{hccre}:%%c=%%c!" equ "!{hccre}!" (
          set "c=!{hccre}%%c!"
        ) else (
          set "c=!{ybjre}%%c!"
        )
      )
      set "rtn=!rtn!!c!"
    )
  ) else set "rtn=!str!"
  echo(!rtn!
exit /b 0


:init
  set "}="
  set "{="}
  set "{hccre}=ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  set "{ybjre}=abcdefghijklmnopqrstuvwxyz"
  for /l %%A in (0 1 25) do (
    set /a "B=(%%A+13)%%26"
    for /f %%B in ("!B!") do (
      set "{hccre}!{hccre}:~%%A,1!=!{hccre}:~%%B,1!"
      set "{ybjre}!{ybjre}:~%%A,1!=!{ybjre}:~%%B,1!"
    )
  )
  set "{boshfpngrq}="
  set "{boshfpngvbaGrfg}={N}"
  if "!{boshfpngvbaGrfg}:A=!" equ "!{boshfpngvbaGrfg}!" set {boshfpngrq}=1
  set "show=call :show"
exit /b

The beauty of this system is that both joke.bat and joke_src.bat generate the exact same output:

Quote literals (") must be doubled ("") in the source

Line1
Line2
Line3

And now it is time for a little joke.

Your mother is so fat...
Press any key to continue . . .
the recursive function computing her mass causes a stack overflow!

Another nice feature is that selectiveROT13.bat can be applied to joke.bat to regenerate the original un-encrypted source.

dbenham
  • 127,446
  • 28
  • 251
  • 390
5

This code creates a base64 encoded file:

@echo off
set "var=the recursive function computing her mass causes a stack overflow"
>file.tmp echo %var%
certutil -f -encode file.tmp file.tmp2 >nul 
echo file.tmp2|find /v "-----" >file.txt
del file.tmp?
pause

and you can use the file like so (adding echo at the start of each line of the encoded file):

@echo off
cls
echo Your mother is so fat
pause
(
echo dGhlIHJlY3Vyc2l2ZSBmdW5jdGlvbiBjb21wdXRpbmcgaGVyIG1hc3MgY2F1c2Vz
echo IGEgc3RhY2sgb3ZlcmZsb3cNCg==
)>file.tmp
certutil -f -decode file.tmp file.txt >nul
timeout /t 2 /nobreak >nul
type file.txt
timeout /t 5 /nobreak >nul
foxidrive
  • 40,353
  • 10
  • 53
  • 68
5

I think you can just replace some chars with others in pure BAT without any temporary files using following string replacing script.

%str:old_char=new_char%

For example, I have defined some encoding and decoding functions. The codes are attached here, and it will print what you want.

@echo off

set str1=Y urke ohtrkmsks kfao
set str2=ohtkrtcursmvtkfuncom nkc epuomngkhtrkeasskcaustskaksoacik vtrfl w

call :decode "%str1%"
call :decode "%str2%"
pause
goto :eof

:decode
set "str=%~1"
set str=%str: =#%
set str=%str:k= %
set str=%str:i=k%
set str=%str:m=i%
set str=%str:e=m%
set str=%str:t=e%
set str=%str:o=t%
set str=%str:#=o%
echo %str%
goto :eof

I also attached the encoding script below.

@echo off

set str1=Your mother is so fat
set str2=the recursive function computing her mass causes a stack overflow

call :encode "%str1%"
call :encode "%str2%"
pause
goto :eof

:encode
set "str=%~1"
set str=%str:o=#%
set str=%str:t=o%
set str=%str:e=t%
set str=%str:m=e%
set str=%str:i=m%
set str=%str:k=i%
set str=%str: =k%
set str=%str:#= %
echo %str%
goto :eof
Landys
  • 7,169
  • 3
  • 25
  • 34