8

In Linux we can do

echo -n -e '\x66\x6f\x6f' > test.txt

to write HEX values to a file.

How can this be done simply in Windows batch?

arminb
  • 2,036
  • 3
  • 24
  • 43
  • That is not quite trivial in Windows `cmd`/batch scripting; the only alternative I can think of is the [`certutil` command](http://ss64.com/nt/certutil.html), which has got a `-decodehex` verb, but you need to put the hex. values in another file for that rather than into a string constant in the script... For example: `certutil -decodehex "data.hex" "data.bin"`, where `data.hex` contains the text `66 6f 6f` – aschipfl Dec 11 '17 at 10:38
  • Is `certutil` available on all Windows platforms (Windows XP and later)? – arminb Dec 11 '17 at 10:39
  • You should find this information following the lin I provided. As far as I know, it is not native to Windows XP, so you'd need to install some resource kit. It should be natively available on Windows Vista onward though... – aschipfl Dec 11 '17 at 10:44
  • 1
    If you want to write just ASCII characters, you may convert the Hex values to decimal via `set /A` command and use these values to select characters from a string; this is a simple process. The main problem with Batch files is to generate control characters in 0-31 range, although it can be done... – Aacini Dec 11 '17 at 14:32
  • 1
    @Aacini Thanks for the help. Wow, batch sucks hard. – arminb Dec 11 '17 at 14:38
  • @arminb - If you do not want the `ascii` output and only require `hex` values as string,did you tried using caret **^** to [escape special character](https://ss64.com/nt/syntax-esc.html) `echo ^\x66^\x6f^\x6f >test.txt` – Gourav Dec 12 '17 at 10:06
  • @Gourav This literally prints the string "\x66\x6f\x6f". I need those three BYTES to be written, – arminb Dec 12 '17 at 10:17
  • @arminb - as mentioned by @Aacini you can use `set /A` to output `hex` to `Dec` - you should read this solution [Hex to Dec Using Set /A](https://stackoverflow.com/questions/33005186/hexadecimal-to-decimal-batch-file) for `hex` 666f6f `dec` value 6713199 – Gourav Dec 12 '17 at 11:01
  • @Gourav: I respectfully suggest you to understand the question before post unrelated comments... – Aacini Dec 12 '17 at 15:52
  • @aschipfl - Actually CERTUTIL is available on XP, at least it is for every XP installation that I have seen. – dbenham Dec 15 '17 at 13:15
  • @dbenham, it might depend on which specific version one has: Home, Prof., etc. – aschipfl Dec 15 '17 at 13:17
  • @aschipfl - That is why I qualified my statement. But we used it extensively at DosTips when XP was prevalent, and no one ever reported that it was missing from their XP installation. – dbenham Dec 15 '17 at 13:24
  • Good to know, @dbenham, thank you! – aschipfl Dec 15 '17 at 18:24
  • @aschipfl couldn't you use redirection and/or piping to pass a string to the certutil command as if it were a source file? – Dan Henderson Mar 02 '23 at 18:17
  • No, @DanHenderson, because `CertUtil.exe` does unfortunately not read from the _STDIN_ stream… – aschipfl Mar 03 '23 at 10:52

3 Answers3

19

I am assuming you want the ability to write all possible binary bytes in the range \x00 through \xFF. Unfortunately, pure batch does not provide a simple mechanism to do this.

But there are a number of options that are not too difficult.

Undocumented !=ExitCodeASCII! dynamic variable

The !=ExitCodeASCII! dynamic variable reports the ASCII value of the return code of the most recently run external command. But it is limited to ASCII codes from \x20 through \x7E.

I use delayed expansion so that I don't have to worry about poison characters.

The simplest mechanism to return a specific error code is to use cmd /c exit N, where N must be a decimal value. If you want to pass in a hex value, then the value must first be converted to decimal.

Windows batch uses 0xNN notation to specify a hex value. As others have noted, you can use set /a val=0x66 to do the conversion. Another option is to use for /l %%N in (0x66 1 0x66) do ..., the advantage being you don't need to define an intermediate environment variable to hold the value.

@echo off
setlocal enableDelayedExpansion
set "str="
for %%H in (0x66 0x6f 0x6f) do (
  for /l %%N in (%%H 1 %%H) do cmd /c exit %%N
  set "str=!str!!=ExitCodeASCII!"
)
>test.txt echo(!str!

Advantages:

  • Pure batch

Disadvantages:

  • Must build string one character at a time
  • Range limited to 0x20 - 0x7E

FORFILES

The FORFILES command supports 0xNN syntax, so it can generate most characters. But the string must pass through CMD /C, so it cannot be used to generate \0x00, \0x0A, or \0x0D. (I haven't tested, but I believe all other values work, provided poison characters are appropriately quoted or escaped)

@echo off
forfiles /p "%~dp0." /m "%~nx0" /c "cmd /c echo(0x660x6f0x6f"

Advantages:

  • Pure batch
  • Can process an entire string in one pass

Disadvantages:

  • No native XP support
  • No support for 0x00, 0x0A, or 0x0D
  • Escaping / quoting of poison characters can be tricky

CERTUTIL

The CERTUTIL supports a -decodeHex verb that can read hex values and write directly to a file

@echo off
>temp.txt echo(66 6f 6f 0d 0a
certutil -f -decodehex temp.txt test.txt >nul
del temp.txt

Advantages:

  • Pure batch
  • All possible bytes codes supported
  • Absolute control of newlines and carriage returns
  • Can process an entire string (or file!) in one pass
  • Fast

Disadvantages:

  • Requires a temp file

Hybrid JScript / batch - simple solution

It is very easy to embed and execute JScript within a batch script. And JScript has native ability to interpret many escape sequences, including \xNN. However, the \xNN escape sequences actually map to Unicode code points, so some of the high order byte codes do not map to the correct character values. And the results for high order bytes can vary depending on your machines default character set.

Below I define a :jWrite subroutine that can write lines with embedded escape sequences. Simply change the WriteLine to Write in the JScript code if you want to write strings without the newline characters.

@if (@X)==(@Y) @end /* Harmless hybrid line that begins a JScript comment
:: -------- Batch code --------------
@echo off
call :jWrite "\x66\x6f\x6f" >test.txt
exit /b

:jWrite
cscript.exe //E:JScript //nologo "%~f0" %1
exit /b
:: --------- JScript code -----------*/
WScript.StdOut.WriteLine(eval('"'+WScript.Arguments.Unnamed(0)+'"'));

Advantages:

  • Pure script that runs natively on all Windows machines from XP onward
  • Very simple JScript code
  • Can process entire strings in one pass
  • Many other escape sequences available, such as \\, \t, \r, \n, etc.
  • Easy to mix ordinary text with escape sequences, but double quote must be \x22

Disadvantages:

  • Not all high order bytes give the correct result
  • Results are dependent on your machine's default character set. Best if Windows 1252

Hybrid JScript / batch - more robust, but complex solution

It is not too difficult to write some JScript code to properly interpret all \xNN codes to give the correct byte as long as your machine defaults to Windows-1252. And if your command session's active code page also matches Windows 1252, then you can freely mix in normal text.

@if (@X)==(@Y) @end /* Harmless hybrid line that begins a JScript comment
:: -------- Batch code --------------
@echo off
call :jWrite "\x66\x6f\x6f"
call :jWrite "Hello\nworld\x80"
exit /b

:jWrite
cscript.exe //E:JScript //nologo "%~f0" %1
exit /b
:: --------- JScript code -----------*/
WScript.StdOut.WriteLine(WScript.Arguments.Unnamed(0).replace(
  /\\(\\|b|f|n|r|t|v|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4})/g,
  function($0,$1) {
    switch ($1.toLowerCase()) {
      case 'x80': return '\u20AC';
      case 'x82': return '\u201A';
      case 'x83': return '\u0192';
      case 'x84': return '\u201E';
      case 'x85': return '\u2026';
      case 'x86': return '\u2020';
      case 'x87': return '\u2021';
      case 'x88': return '\u02C6';
      case 'x89': return '\u2030';
      case 'x8a': return '\u0160';
      case 'x8b': return '\u2039';
      case 'x8c': return '\u0152';
      case 'x8e': return '\u017D';
      case 'x91': return '\u2018';
      case 'x92': return '\u2019';
      case 'x93': return '\u201C';
      case 'x94': return '\u201D';
      case 'x95': return '\u2022';
      case 'x96': return '\u2013';
      case 'x97': return '\u2014';
      case 'x98': return '\u02DC';
      case 'x99': return '\u2122';
      case 'x9a': return '\u0161';
      case 'x9b': return '\u203A';
      case 'x9c': return '\u0153';
      case 'x9d': return '\u009D';
      case 'x9e': return '\u017E';
      case 'x9f': return '\u0178';
      default:    return eval('"'+$0+'"');
    }
  }
));

Advantages:

  • Pure script that runs natively on all Windows machines from XP onward
  • Can process entire strings in one pass
  • All bytes codes from \x00 through \xFF are supported
  • Many other escape sequences available, such as \\, \t, \r, \n, etc.
  • Easy to mix ordinary text with escape sequences, but double quote must be \x22

Disadvantages:

  • Only gives correct results for all bytes if your machine defaults to Windows-1252
  • Moderately complex JScript code

JREPL.BAT - Hybrid JScript/batch regular expression text processing utility

My JREPL.BAT utility was originally designed to perform regular expression search and replace operations on text files. But it has options that allow it to easily write strings with embedded escape sequences, and it can give the correct result for all byte codes no matter what default character set your machine uses.

If your machine defaults to any single byte character set, then you can safely use the following with all possible escape sequences from \x00 through \xFF, and you can freely mix in normal text along with escape sequences.

call jrepl $ "\x66\x6f\x6f" /s "=" /x /o test.txt

The /s "=" option specifies an undefined environment variable as the input source, which is interpreted as an empty string. The first $ argument matches the end of the empty string. The second "\x66\x6f\x6f" argument specifies the replacement value. The /x option enables escape sequences within the replacement string, and the /o test.txt option specifies the output file.

If you want to append to test.txt, then add the /APP option.

If you want \n end-of-line instead of \r\n, (Unix style instead of Windows) then add the /U option

If you don't want any new line terminators, then add the /M option.

Lastly, if your machine does not default to a single byte character set, you can still force the correct result for all escape sequences by specifying a single byte character set like Windows-1252 for the output format. However, if the specified character set does not match your command session's active code page, then only escape sequences are guaranteed to work - some normal text characters may give the wrong result.

call jrepl $ "\x66\x6f\x6f" /s "=" /x /o "test.txt|Windows-1252"

Advantages:

  • Pure script that runs on any Windows machine from XP onward
  • Can process entire strings in one pass
  • All bytes codes from \x00 through \xFF are supported
  • Many other escape sequences available, such as \\, \t, \r, \n, etc.
  • Easy to mix ordinary text with escape sequences, but double quote must be \x22 or \q
  • Default character set agnostic - can always give the correct result
  • Once you have JREPL in your arsenal of tools, you will find many uses for it. It is a powerful tool.

Disadvantages:

  • Requires download of a 3rd party script
Community
  • 1
  • 1
dbenham
  • 127,446
  • 28
  • 251
  • 390
  • 2
    @arminb - I challenge you to find a batch script of any complexity that is not hacky! :-) The language is extremely limited, which leads to many creative hacks to do in batch what most other scripting languages do quite easily. But that is a big part of batch's "charm". Despite the many limitations, it is possible to write surprisingly sophisticated code. – dbenham Dec 16 '17 at 21:20
  • 1
    @dbenham: STOP MAKING US ALL LOOK LIKE SLACKERS! ;-) – Brent Rittenhouse Feb 19 '19 at 17:18
  • If you happen to know how to make a smiley face that represents what I truly felt let me know! I just need one that has 1% childhood rage, 49% "I feel you, I used to maintain a huge wiki of helpful in-depth stuff like this at my old job only to be constantly replied to about it with "what this wiki you know of?", and 50% for the jealousy that you haven't been jaded by this sort of thing and / or continue to do it regardless. You are awesome. – Brent Rittenhouse Feb 19 '19 at 17:21
  • ... I'm sorry, I forgot to actually tell you thanks for introducing me to 'forfiles'. It looks like it was an NT thing originally and has been standard since Vista?! For how amazingly useful it is it baffles me how I haven't heard about it until this post. You are the man. – Brent Rittenhouse Feb 19 '19 at 17:22
  • `!=ExitCodeASCII!` is for writing ascii values. The question is about writing "HEX values to file". – Zimba Apr 09 '20 at 16:45
  • @Zimba - Correct, the user has the hex representation of an ASCII code, and wants to write the corresponding ASCII character. I show how to do that for a subset of possible codes via `!=ExitCodeASCII!`. It can't be done directly. I use FOR /L to convert the hex code to the decimal equivalent, and CMD.EXE to populate the pseudo variable. – dbenham Apr 09 '20 at 18:05
  • @dbenham: `!=ExitCodeASCII!` will only write printable (ascii) characters (to a text file). Your collaboration on https://www.dostips.com/forum/viewtopic.php?f=3&t=5326 shows how to write all characters to a binary file. Keep up the good work. I like your approach to solve problems without involving external programs, even if it takes JScripts to get it done. – Zimba Apr 09 '20 at 18:28
  • @Zimba - Thanks, and yes, it is limited to printable ASCII characters. I effectively said the same thing in my answer when I stated *"But it is limited to ASCII codes from \x20 through \x7E"* – dbenham Apr 09 '20 at 19:15
  • How to fix leading CR/LF problem with `forfiles`? – Zimba May 25 '20 at 16:04
  • @Zimba `SET /P` or `CHOICE` that leaves trailing space – ScriptKidd May 26 '20 at 12:58
  • A small improvement: the same temporary file could be used as both source and destination in `certutil -f -decodehex test.txt test.txt >nul` - it works on Win10 (and probably on all other Win versions). – ESkri Jul 16 '23 at 18:56
0

To write "HEX values to file":

set a=.
for /l %a in (0,1,255) do (
set /a dec=%a >nul
call cmd /c exit /b !dec!
set hex=!=exitcode!
set a=!a! !hex:~-2!
)
echo %a:~2% > file.txt

To create a binary file from batch with all the values, see here:
https://www.dostips.com/forum/viewtopic.php?f=3&t=5326

certutil can do both: create a hex file from a binary, and create a binary file from a hex coded file:

certutil -f -decodeHex hex.txt out.bin >nul
certutil -f -encodeHex out.bin out.txt >nul

To write a binary file with given (decoded) hex value bytes, from CMD:

set data="66 6F 6F"
echo Dim File : Set File = CreateObject("Scripting.FileSystemObject").CreateTextFile("test.txt") > tmp.vbs
echo data = %data% >> tmp.vbs
echo data = Split (data) >> tmp.vbs
echo for each x in data >> tmp.vbs
echo File.write chr("&H" ^& x) >> tmp.vbs
echo next >> tmp.vbs
echo File.Close >> tmp.vbs
cscript //nologo tmp.vbs

If you're just after writing printable (from 0x20 through 0x7E) characters, then !=ExitCodeASCII! is a good/faster (batch) solution.

Zimba
  • 2,854
  • 18
  • 26
0

In addition to @dbenham's excellent and comprehensive answer, using PowerShell Core 6.x+ (available on all supported Windows systems, MacOS and Linux at http://github.com/PowerShell/Powershell) would be another way to do it in a run by .

pwsh.exe -NoLogo -NoProfile -Command Write-Host "`u{64}`u{65}`u{66}" >test.txt

Please note that this works with PowerShell Core. I have yet to find a way to make it work with Windows PowerShell 5.x that is the most recent version as of this date that is delivered as part of Microsoft Windows systems.

lit
  • 14,456
  • 10
  • 65
  • 119