modern reply.exe created using only batch

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
carlos
Expert
Posts: 488
Joined: 20 Aug 2010 13:57
Location: Chile
Contact:

modern reply.exe created using only batch

#1 Post by carlos » 30 Jan 2014 01:13

Edit: minor changes to the batch code, and to my comments in this post.

Hello.

The title of this topic is for celebrate the creation of technique for write binary data using only batch developed in this topic:
http://www.dostips.com/forum/viewtopic.php?f=3&t=5326

For this I write a little tool reply.exe (1536 bytes) as replacement of old reply.com found here: http://support.microsoft.com/kb/77457
because 16 bits programs are not supported on 64 bit window system.

the source code of it is at the end of this post.

The program accept a keyboard input and return a numeric code. If the input is a extended key, example a arrow key, it sum 256 to the numeric code.

I'm interesed on creation of binary data using batch since 2010 and I read many things about this. I use succesfully utilities that generates .com files using only ascii files from authors like herbert kleebauer, laura fairhead, jim tucker, creation using debug, all it for 16 bits, and for 32 bits creation using vbscript (cscript, mshta), certutil. Also I wasted many time writing a utility called bhx that combine many techniques of it with focus on give support to windows 2000 and above systems of 64 bits, also combining the usage of cabinet files for reduce the file size.
But, now, finally is posible using only batch. (I think add this as module to bhx for a easy script generation). I celebrate this advance in the batch posting the creation of reply.exe using only batch. This method is good because not create a debug or vbs script. It write directly to the file using batch, not interpretation of a script is involved.

This generation of the executable using the function :genchr in this script is a prototype, but can serve as a guide for creation a binary using only batch, you only need change the hex data and the filenames. Note: I optimized for support only hex data with length of 60 characters. I choose this length in bhx and in this code because in the past I remember post some hex data on alt.msdos.batch.nt and get the script spoiled because large line length.

create_reply.cmd

Code: Select all

@echo off
REM creation of reply.exe a utility for get a user input key and know the value of it
setlocal enabledelayedexpansion

set "out=reply.cab"
>"!out!" type nul

echo creating reply.exe ...
call :w "4D53434600000000B8010000000000002C00000000000000030101000100"
call :w "0000000000004600000001000100000600000000000000003E44B31C2000"
call :w "7265706C792E65786500F8C5FE566A010006434BF38D9AC0C0CCC0C0C002"
call :w "C4FFFF3330EC6080000706C2A00188F9E477F1316CE13CABB883D1E7AC62"
call :w "484666B14241517E7A5162AE4272625E5E7E894252AA4251699E42669E82"
call :w "8B7FB0426E7E4AAA1E2F2F970AD48C005706061F462614731F30F0337333"
call :w "B2A15A2600C50A50D781D84C10773330206806038878395009334CA30032"
call :w "0DA72000689E0D117E2515D800CD15C123AF57925A51020D43985F185083"
call :w "01E4B404BD94C49244207B015400AC8605551DD0AB07C8756768E7D3C637"
call :w "20F326BC8804929DAE7FBA5DFFB41EE06F1105F2763C0012DDBE7F2C4FF0"
call :w "B7B002992FF980C40B4320D178808111A63AE0853A50A4F908CBC9C360D3"
call :w "40C64E00A7A3801720534E1E06A52D551B050730ED00A53D14884963A380"
call :w "B6204001C14E5180A4DB1824B17A20DB4301BBDE7CA0780554AE4701B758"
call :w "6E71597251895E4A4E0E90139F9E5A929C0164A45664963064A716E5A5E6"
call :w "181B41255D81620145F9C9A9C5C5A35143170000"
del *.chr
expand "!out!" reply.exe >nul
del "!out!"

echo Press a key ...
reply.exe
echo Key pressed: %errorlevel%
pause

goto :eof

:genchr
REM This code creates one single byte. Parameter: int
REM Teamwork of carlos, penpen, aGerman, dbenham
REM Tested under Win2000, XP, Win7, Win8
set "options=/d compress=off /d reserveperdatablocksize=26"
if %~1 neq 26  (type nul >t.tmp
makecab %options% /d reserveperfoldersize=%~1 t.tmp %~1.chr >nul
type %~1.chr | (
(for /l %%N in (1 1 38) do pause)>nul&findstr "^">temp.tmp)
>nul copy /y temp.tmp /a %~1.chr /b
del t.tmp temp.tmp
) else (copy /y nul + nul /a 26.chr /a >nul)
goto :eof

:w
set "line=%~1"
::get length upto 60 characters
::Function for get the length originally writted by jeb. Modified.
(set "z=0" &set "s=a!line!" &for %%a in (
64 32 16 8 4 2 1) do (set /a "z|=%%a"
for %%b in (!z!) do if "!s:~%%b,1!"=="" set /a "z&=~%%a"))
set /a "limit=z-2"
for /l %%# in (0,2,%limit%) do (
set /a "chr=0x!line:~%%#,2!"
if not exist !chr!.chr call :genchr !chr!
copy /y "!out!" /b + !chr!.chr /b "!out!" >nul
)
goto :eof



reply.c
// Compiled with tiny c for get a small executable

Code: Select all

#include <windows.h>
#include <conio.h>

void _start(void)
{
    int ret = getch();
    if (!ret || 0xE0 == ret) {
   ret = 256 + getch();
    }
    exit(ret);
}

void avoid_false_positive() {
   /* avira can detect a false positive on executables files with less bytes */
   /* this fix it */
    ExitProcess(0);
}

h.k
Posts: 2
Joined: 30 Jan 2014 04:39

Re: modern reply.exe created using only batch

#2 Post by h.k » 30 Jan 2014 05:09

carlos wrote:

> This generation of the executable using the function :genchr in
> this script is a prototype, but can serve as a guide for creation
> a binary using only batch, you only need change the hex data and
> the filenames.


Nice work. But why do you calculate the length of the line.
Wouldn't it be easier to extract hex values until the
end of line is reached:

Code: Select all

:w
set "line=%~1"
:loop
set /a "chr=0x!line:~0,2!"
if not exist !chr!.chr call :genchr !chr!
copy /y "!out!" /b + !chr!.chr /b "!out!" >nul
set "line=!line:~2!"
if not [!line!]==[] goto :loop
)


But I still prefer to use certutil.exe instead of creating
256 one byte files using makecab.exe. It is cleaner and
faster. I also don't think it is necessary to compress the
exe file, because most things can be done in a 1024 byte
exe file. Here an example using certutil:

Code: Select all

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

@echo off
:: swap left and right mouse button

certutil -f -decode %~f0 swap.exe>nul
swap.exe
del swap.exe

goto :eof

-----BEGIN CERTIFICATE-----
TVpgAQEAAAAEAAAA//8AAGABAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAoAAAAA4fug4AtAnNIbgBTM0hTmljZSB0byBtZWV0IHNvbWVi
b2R5IHdobyBpcyBzdGlsbCB1c2luZyBET1MsDQpidXQgdGhpcyBwcm9ncmFtIHJl
cXVpcmVzIFdpbjMyLg0KJFBFAABMAQEAUHmlNgAAAAAAAAAA4AAPAQsBBQwAAgAA
AAAAAAAAAACWEAAAABAAAAAgAAAAAEAAABAAAAACAAAEAAAAAAAAAAQAAAAAAAAA
ACAAAAACAAAAAAAAAgAAAAAAEAAAEAAAAAAQAAAQAAAAAAAAEAAAAAAAAAAAAAAA
EBAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAQAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALnRleHQAAACyAAAAABAAAAACAAAAAgAA
AAAAAAAAAAAAAAAAIAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgEAAAAAAAAIgQAAAAAAAA
WBAAAAAAAAAAAAAATBAAAAAQAACAEAAAAAAAAAAAAAByEAAACBAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAVVNFUjMyLmRsbAAAYBAAAAAAAAAAAFN3YXBNb3VzZUJ1dHRv
bgBLRVJORUwzMi5kbGwAAIgQAAAAAAAAAABFeGl0UHJvY2VzcwBqAP8VABBAAIPw
AXQHUP8VABBAAGoA/xUIEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAA==
-----END CERTIFICATE-----

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

ceruti.exe can also be used to process binary files using
batch programs. All you need are 3 subroutines: 

:fopen
:fclose
:getc_hex

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

@echo off
setlocal enabledelayedexpansion

set infile=a.txt
set outfile=b.txt
set tmp0=_tmp0.tmp

if exist %tmp0% del %tmp0%

call :fopen %infile%
echo %infile% opened as input file

:::::::::::::::::::::::::::::::::::::::::::::::::::::
:: copy byte 100 - 200 from infile to tmp0 (hexformat)

for /l %%i in (100,1,200) do (
  call :getc_hex char %%i
  echo  !char!>>%tmp0%)
 
:::::::::::::::::::::::::::::::::::::::::::::::::::::
:: append byte 1200 - 1000 from infile to tmp0 (hexformat)
:: read backward

for /l %%i in (1200,-1,1000) do (
  call :getc_hex char %%i
  echo  !char!>>%tmp0%)
 
:::::::::::::::::::::::::::::::::::::::::::::::::::::
:: convert hexfile tmp1 to binary outfile
certutil -f -decodehex %tmp0% %outfile% >nul
if exist %tmp0% del %tmp0%

call :fclose

goto :eof


:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:fopen
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
set tmp1=_tmp1.tmp
set tmp2=_tmp2.tmp
set fsize=%~z1
set cache=-1
certutil  -f -encodehex %1 %tmp1% 4 >nul
set n=0
if exist %tmp2% del %tmp2%

for /f "tokens=1-16" %%a in (%tmp1%) do (
 set /p =%%a%%b%%c%%d%%e%%f%%g%%h%%i%%j%%k%%l%%m%%n%%o%%p<nul >>%tmp2%
 set /a n=n+1
 if !n!==64 echo.>>%tmp2% & set n=0)
 
goto :eof

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:fclose
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
del %tmp1%
del %tmp2%
goto :eof


:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:getc_hex
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
if %2 GEQ %fsize% set /a %1=-1 & goto :eof
set /a i=%2/1024 & set /a j=2*(%2%%1024)
if %i%==%cache% goto :skip
echo                                       read cache line %i%
set cache=%i%
set skip=
if %i% GEQ 1 set skip="skip=%i%"
for /f %skip% %%i in (%tmp2%) do (
  set line=%%i & goto :skip)
:skip
call set %1=%%line:~%j%,2%% 
goto :eof
 
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


dbenham
Expert
Posts: 2261
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: modern reply.exe created using only batch

#3 Post by dbenham » 30 Jan 2014 07:00

@h.k - But certutil.exe is not standard with XP. And processing a string in a GOTO loop until done is indeed simpler, but much slower. The strlen function is exceptionally fast.

@carlos - Very nice. But it is much faster to redirect TYPE output than it is to use COPY /B.

Here is a version that is 4 times faster on my machine when writing the file. Of course it is not very noticeable because the bottle neck is the initial generation of the .CHR files. But if the files are saved somewhere, (maybe a folder within %userprofile%?), then the performance boost is significant on subsequent runs.

Code: Select all

@echo off
REM creation of reply.exe a utility for get a user input key and know the value of it
setlocal enabledelayedexpansion

set "out=reply.cab"
echo creating reply.exe ...
>"!out!" (
call :w "4D53434600000000B8010000000000002C00000000000000030101000100"
call :w "0000000000004600000001000100000600000000000000003E44B31C2000"
call :w "7265706C792E65786500F8C5FE566A010006434BF38D9AC0C0CCC0C0C002"
call :w "C4FFFF3330EC6080000706C2A00188F9E477F1316CE13CABB883D1E7AC62"
call :w "484666B14241517E7A5162AE4272625E5E7E894252AA4251699E42669E82"
call :w "8B7FB0426E7E4AAA1E2F2F970AD48C005706061F462614731F30F0337333"
call :w "B2A15A2600C50A50D781D84C10773330206806038878395009334CA30032"
call :w "0DA72000689E0D117E2515D800CD15C123AF57925A51020D43985F185083"
call :w "01E4B404BD94C49244207B015400AC8605551DD0AB07C8756768E7D3C637"
call :w "20F326BC8804929DAE7FBA5DFFB41EE06F1105F2763C0012DDBE7F2C4FF0"
call :w "B7B002992FF980C40B4320D178808111A63AE0853A50A4F908CBC9C360D3"
call :w "40C64E00A7A3801720534E1E06A52D551B050730ED00A53D14884963A380"
call :w "B6204001C14E5180A4DB1824B17A20DB4301BBDE7CA0780554AE4701B758"
call :w "6E71597251895E4A4E0E90139F9E5A929C0164A45664963064A716E5A5E6"
call :w "181B41255D81620145F9C9A9C5C5A35143170000"
)
del *.chr
expand "!out!" reply.exe >nul
del "!out!"

echo Press a key ...
reply.exe
echo Key pressed: %errorlevel%
pause

goto :eof

:genchr
REM This code creates one single byte. Parameter: int
REM Teamwork of carlos, penpen, aGerman, dbenham
REM Tested under Win2000, XP, Win7, Win8
set "options=/d compress=off /d reserveperdatablocksize=26"
if %~1 neq 26  (type nul >t.tmp
makecab %options% /d reserveperfoldersize=%~1 t.tmp %~1.chr >nul
type %~1.chr | (
(for /l %%N in (1 1 38) do pause)>nul&findstr "^">temp.tmp)
>nul copy /y temp.tmp /a %~1.chr /b
del t.tmp temp.tmp
) else (copy /y nul + nul /a 26.chr /a >nul)
goto :eof

:w
set "line=%~1"
::get length upto 60 characters
::Function for get the length originally writted by jeb. Modified.
(set "z=0" &set "s=a!line!" &for %%a in (
64 32 16 8 4 2 1) do (set /a "z|=%%a"
for %%b in (!z!) do if "!s:~%%b,1!"=="" set /a "z&=~%%a"))
set /a "limit=z-2"
for /l %%# in (0,2,%limit%) do (
set /a "chr=0x!line:~%%#,2!"
if not exist !chr!.chr call :genchr !chr!
type !chr!.chr
)
goto :eof


Dave Benham

carlos
Expert
Posts: 488
Joined: 20 Aug 2010 13:57
Location: Chile
Contact:

Re: modern reply.exe created using only batch

#4 Post by carlos » 30 Jan 2014 07:16

h.k wrote:Nice work. But why do you calculate the length of the line.
Wouldn't it be easier to extract hex values until the
end of line is reached:

Code: Select all

if not [!line!]==[] goto :loop


But I still prefer to use certutil.exe instead of creating
256 one byte files using makecab.exe. It is cleaner and
faster. I also don't think it is necessary to compress the
exe file, because most things can be done in a 1024 byte
exe file. Here an example using certutil:
[/code]


@h.k: I calculate the length with a function that not use a goto loop, because goto loop are slower. And this post is for celebrate the technique of create binary data using only batch, not certutil. Certutil have the disadvantage that is not present on windows xp.
Also I reduce the size of the generated hex data using a cabinet file. Please compare this: The hex data of your swap.exe in base 64 are 1412 bytes. The same swap.exe compressed as mszip in a cabinet produce a hex data with 806 bytes.
1412-806 = 606 bytes saved.

This post is not fow show the way of create binary data using certutil, vbscript or debug script,. Is for a show a way using only batch, the technique recently developed. For example, other user can post to your message something like this: "I prefer use vbscript instead certutil because certutil is not present on windows xp unlike vbscript". Or other user: "I prefer the script generated by bhx 3.1 because will works from windows 2000". Obviosuly you can choose the method of creating the binary data according to multiple factors. But this post is for only use batch.

@dbenham: thanks for the info avout type vs copy, and the code example. If you look my code it use some cache system, because only create the .chr that not exist, and keep the created until the script is done, this is for avoid create all 255 .chr files. But your use of type inside a code block redirecting in your code is similar to write to a buffer vs the copy /b that is compare to direct write i/o. Always buffered write system are more faster. Thanks again for the code example.

Aacini
Expert
Posts: 1605
Joined: 06 Dec 2011 22:15
Location: México City, México

Re: modern reply.exe created using only batch

#5 Post by Aacini » 31 Jan 2014 10:45

In order to celebrate the creation of this technique, I modified my ExtractBinaryFile subroutine to use the pure Batch method to write the binary file instead of the original JScript code. I like ExtractBinaryFile subroutine because you just take the hexadecimal representation of the binary file and insert it in the Batch file surrounded by a pair of delimiting lines, and it also compress groups of binary zeros.

I also wrote a new auxiliary program for this occasion; this program, called GetInput.exe, allows to get input from both keyboard and mouse and return it via errorlevel. A key press is returned as a positive value and a mouse click as a negative value that indicate the mouse cursor position in the screen using this formula: errorlevel = -(line<<16 + column). An interesting point of this program (besides the fact that it allows to use the mouse as input device in Batch files) is that it can detect any keystroke, that is, besides all extended keys it also report keys that usually are not pressed alone, like Shift, Ctrl, Alt, Caps-Lock, etc. and even a Ctrl-C combination without cancel the Batch file; you may consult Virtual-Key Codes for a complete description of the values returned this way. Just for a change, I included the assembly source code of this program below.

It is interesting to note that although a .cab compressed file is much lesser than the original file, in the particular case of small .exe files they contain many groups of binary zeros that are compressed/expanded by ExtractBinaryFile subroutine, so the makecab-compression/expand-expantion steps are a waste in this case. For example, GetInput.exe file is 1536 bytes long and GetInput.exe.cab is just 552, but GetInput.exe.hex is 1080 bytes and GetInput.exe.cab.hex is 1069 bytes, about 1% lesser!

EDIT - 2014/01/31: I fixed a detail in GetInput.exe auxiliary program: it now cancel Quick Edit mode and preserve the original mode at exit; both the hexadecimal version and the source code at end include this change now.
EDIT - 2014/02/01: I modified GetInput.exe program to also report right-button mouse clicks and modified the Batch code in order to achieve a more precise identification of the key pressed.
EDIT - 2014/02/04: I modified GetInput.exe program to also report Alt-key and Shift-extended key combinations and to omit non-useful keys, like Shift-, Ctrl- or Alt- keys alone, or CapsLock. Full details in both Batch and assembler source code.

Code: Select all

@echo off

rem Example of GetInput.exe keyboard/mouse input auxiliary program
rem Antonio Perez Ayala

if not exist GetInput.exe call :ExtractBinaryFile GetInput.exe.cab

setlocal DisableDelayedExpansion
set Ascii= !"#$%%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
setlocal EnableDelayedExpansion
for %%a in ("7=Bell" "8=BackSpc" "9=Tab" "10=LineFeed" "13=Enter" "26=Sub" "27=Esc") do (
   for /F "tokens=1,2 delims==" %%b in (%%a) do set "control[%%b]=(%%c)"
)
for %%a in ("33=PageUp" "34=PageDown" "35=End" "36=Home" "37=LeftArrow" "38=UpArrow" "39=RightArrow" "40=DownArrow"
            "45=Ins" "46=Del"
            "112=F1" "113=F2" "114=F3" "115=F4" "116=F5" "117=F6" "118=F7" "119=F8" "120=F9" "121=F10" "122=F11" "123=F12"
           ) do (
   for /F "tokens=1,2 delims==" %%b in (%%a) do set "extended[%%b]=%%c"
)

cls
echo To end, click in the "X": [X]
echo/
echo Press a key (ESC to end) or click a mouse button
echo/

:nextInput
   GetInput
   set /A "input=%errorlevel%, ctrl=input+32, asc=input-32, alt=input-192, ext=input-256&0xFF"
   if %input% gtr 0 (
      rem Key pressed
      set input=  %input%
      set input=!input:~-3!
      if %input% lss 32 (
         echo !input!:    Ctrl-!Ascii:~%ctrl%,1! !control[%input%]!
      ) else if %input% lss 192 (
         echo !input!:                    Ascii: !Ascii:~%asc%,1!
      ) else if %input% lss 256 (
         echo !input!:                                    Alt-!Ascii:~%alt%,1!
      ) else (
         if %input% lss 512 (
            set extnd=              !extended[%ext%]!
         ) else (
            set extnd=        Shift-!extended[%ext%]!
         )
         echo !input!:                                            !extnd:~-16!
      )
   ) else ( rem %input% leq 0
      rem Mouse button clicked
      set /A "input=-input, row=input >> 16, col=input & 0xFFFF"
      if !col! lss 32768 (
         echo LEFT @ !row!,!col!
      ) else (
         set /A col-=32768
         echo                                                                 @ !row!,!col! RIGHT
      )
   )
if %input% neq 27 goto nextInput
goto :EOF


rem Extract Binary File from hexadecimal digits placed in a "resource" in this .bat file

:ExtractBinaryFile filename.ext[.cab]
setlocal EnableDelayedExpansion
ECHO Creating %1, please wait...
set "start="
set "end="
for /F "tokens=1,3 delims=:=>" %%a in ('findstr /N /B "</*resource" "%~F0"') do (
   if not defined start (
      if "%%~b" equ "%~1" set start=%%a
   ) else if not defined end set end=%%a
)
(for /F "skip=%start% tokens=1* delims=:" %%a in ('findstr /N "^" "%~F0"') do (
   if "%%a" == "%end%" goto decodeHexFile
   echo %%b
)) > "%~1.hex"
:decodeHexFile

rem Modified code based on :genchr subroutine
type nul > t.tmp
(for /F "usebackq" %%a in ("%~1.hex") do (
   set input=%%a
   set i=0
   for /L %%I in (0,2,120) do for %%i in (!i!) do if "!input:~%%i,1!" neq "" (
      set hex=!input:~%%i,2!
      set /A i+=2
      if "!hex:~0,1!" neq "[" (
         set /A chr=0x!hex!
         if not exist !chr!.chr call :genchr !chr!
         type !chr!.chr
      ) else (
         for /L %%J in (1,1,5) do for %%i in (!i!) do if "!input:~%%i,1!" neq "]" (
            set "hex=!hex!!input:~%%i,1!"
            set /A i+=1
         )
         if not exist 0.chr call :genchr 0
         for /L %%J in (1,1,!hex:~1!) do type 0.chr
         set /A i+=1
      )
   )
)) > "%~1"
del *.chr
del t.tmp temp.tmp
del "%~1.hex"

rem Expand created file if extension is .cab
set "filename=%~1"
if /I "%filename:~-4%" equ ".cab" (
   expand "%filename%" "%filename:~0,-4%" > NUL
   del "%filename%"
)

exit /B


:genchr
REM This code creates one single byte. Parameter: int
REM Teamwork of carlos, penpen, aGerman, dbenham
REM Tested under Win2000, XP, Win7, Win8
set "options=/d compress=off /d reserveperdatablocksize=26"
if %~1 neq 26 (
   makecab %options% /d reserveperfoldersize=%~1 t.tmp %~1.chr > nul
   type %~1.chr | ( (for /l %%N in (1,1,38) do pause)>nul & findstr "^" > temp.tmp )
   >nul copy /y temp.tmp /a %~1.chr /b
) else (
   copy /y nul + nul /a 26.chr /a >nul
)
exit /B


<resource id="GetInput.exe.cab">
4d534346[4]8302[6]2c[7]0301010001[7]49[3]010001[2]06[8]4444b8962000476574496e7075742e6578650077a0cada32020006434bf38d9a
c0c0ccc0c0c002c4ffff3330ec6080000706c2600310f3c9efe263d8c279567107a3cf59c5908ccc628582a2fcf4a2c45c85e4c4bcbcfc1285a45485
a2d23c85cc3c0517ff6085dcfc94543d5e5e2e15a819b1a97f4f48b24c9e0dc3d3a51b660b02e9a72a8db325807450667206481c6667802b03830f23
13c3ecee8f4130b1070cfc8cdc8cac3c0c4c400e135450008a15a0be1180c8b140a561348301420f334ca300320da7c04002689e0611614332009a2b
81475aaf24b5a20448f33032c0fd02f72bc28804bda294c4924406863d5001b01a1654750e48d11bdafda6f9c883ac6f2fde02399dae7f7a5d7f04fc
2ffdf3e20690dbedfa837702906e7eb00f2c760dc8ee757d1090c5d8ebfa042cb20f2892d65cfb84b134b3b9f60543c943fe6dae9fd2b80f94c87f77
fd02b2b9a42aadf947557d491aeb0286345b06c69aacb4e6170aaf53d2ba5ddf0165146bd603498d3a0920a95bb31848ead5f100c9829ae940b2ba7e
621a2b0323d02890874bf4811ca6d79a20fb98f85b8bffffffffddf50d28cef85bd2809c6ed717403e28784a991bce347cbfd17de37fe90f902be580
62c12f40befdaf2aa0e0002419c0240b98e400933c0ac4a4f8e107d28069a402883b80780e10472840d3880242cd3a05583e225eff6c06d78acc9280
a2fce4d4e262864f0ceea925cef979c5f939a9bec0128081218b1128125c92e29198979203e49b3005a526a6409578e61594963832e43105a3e9ca4e
2dca4bcd3136d24bc9c91991d145750000
</resource>

This is GetInput.asm:

Code: Select all

;   GetInput
;
;       Get console input from both keyboard and mouse
;
;   Antonio Perez Ayala - Version 1.0 - Jan/30/2014
;                       - Version 1.1 - Jan/31/2014 - Preserve console mode, disable quick edit
;                       - Version 1.2 - Feb/01/2014 - Get right button mouse clicks
;                       - Version 1.3 - Feb/04/2014 - Get Alt-keys and Shift-extended keys


        include                 \masm32\include\masm32rt.inc

        Main                    PROTO
        GetStdHandle            PROTO
        GetConsoleMode          PROTO
        SetConsoleMode          PROTO   ;http://msdn.microsoft.com/en-us/library/windows/desktop/ms686033

(v=vs.85).aspx
        ReadConsoleInput        PROTO   ;http://msdn.microsoft.com/en-

us/library/windows/desktop/ms684961%28v=vs.85%29.aspx

    .code

Main    PROC

        LOCAL   hInput          :DWORD
        LOCAL   lpMode          :DWORD
        LOCAL   lpBuffer        :INPUT_RECORD
        LOCAL   lpEvents        :DWORD

        invoke  GetStdHandle, STD_INPUT_HANDLE  ;EAX = console input handle
        mov     hInput, eax                     ;store it
        ;
        invoke  GetConsoleMode, hInput, ADDR lpMode     ;get current console mode
        ;
        mov     eax, lpMode                                     ;EAX = current mode
        or      eax, ENABLE_MOUSE_INPUT + ENABLE_EXTENDED_FLAGS ;enable mouse events + value...
        ;                                                       ;- required to disable quick edit
        and     eax, NOT (ENABLE_QUICK_EDIT_MODE OR ENABLE_PROCESSED_INPUT) ;disable quick edit and Ctrl-C
        invoke  SetConsoleMode, hInput, eax             ;change console mode
        ;
get_event:                                      ;get the next input event
        invoke  ReadConsoleInput, hInput, ADDR lpBuffer, 1, ADDR lpEvents
        ;
        cmp     lpBuffer.EventType, KEY_EVENT   ;is a key event?
        jne     SHORT check_mouse               ;no: continue
        ;
        cmp     lpBuffer.KeyEvent.bKeyDown,FALSE;the key was pressed?
        je      SHORT get_event                 ;no: ignore key releases
        ;
        movzx   eax, lpBuffer.KeyEvent.AsciiChar;get the key Ascii code
        or      ax, ax                          ;is zero?
        jz      SHORT virtual_key               ;yes: is extended key
        ;
        test    lpBuffer.KeyEvent.dwControlKeyState,LEFT_ALT_PRESSED ;Alt- pressed?
        jz      SHORT end_main                                       ;no: continue
        ;
        cmp     ax, 'z'                         ;is digit/letter?
        jg      SHORT end_main                  ;no: continue
        ;
        add     ax, 160                         ;add 160 as Alt-key signature (Alt-A=225)
        cmp     ax, 256                         ;was lowcase letter?
        jl      SHORT end_main                  ;no: continue
        sub     ax, 'a'-'A'                     ;else: convert to upcase
        jmp     SHORT end_main                  ;and return it
        ;
virtual_key:
        mov     ax, lpBuffer.KeyEvent.wVirtualKeyCode   ;get Virtual Key Code
        ;
        cmp     ax, VK_PRIOR                    ;less than 1st useful block?
        jl      SHORT get_event                 ;yes: ignore it
        cmp     ax, VK_DOWN                     ;in 1st useful block?
        jle     SHORT @F                        ;yes continue
        ;
        cmp     ax, VK_INSERT                   ;less than 2nd useful block?
        jl      SHORT get_event                 ;yes: ignore it
        cmp     ax, VK_DELETE                   ;in 2nd useful block?
        jle     SHORT @F                        ;yes: continue
        ;
        cmp     ax, VK_F1                       ;less than 3rd useful block?
        jl      SHORT get_event                 ;yes: ignore it
        cmp     ax, VK_F12                      ;greater than 3rd useful block?
        jg      SHORT get_event                 ;yes: ignore it
@@:
        add     ax, 256                         ;add 256 as extended-key signature
        ;
        test    lpBuffer.KeyEvent.dwControlKeyState,SHIFT_PRESSED   ;Shift- pressed?
        jz      SHORT end_main                                      ;no: continue
        add     ax, 512                                             ;else: add 512 as Shift- signature
        jmp     SHORT end_main                                      ;and return it
        ;
check_mouse:
        cmp     lpBuffer.EventType, MOUSE_EVENT ;is a mouse event?
        jne     get_event                       ;no: ignore event
        ;                                       ;left or right button pressed?
        test    lpBuffer.MouseEvent.dwButtonState, FROM_LEFT_1ST_BUTTON_PRESSED OR RIGHTMOST_BUTTON_PRESSED
        jz      get_event                       ;no: ignore button releases
        ;
        mov     eax, lpBuffer.MouseEvent.dwMousePosition;get click position as row<<16 + col
        test    lpBuffer.MouseEvent.dwButtonState, FROM_LEFT_1ST_BUTTON_PRESSED ;left button?
        jnz     SHORT neg_pos                                                   ;yes: continue
        or      ah, 80H                                 ;else: add 32768 to col for right button
neg_pos:
        neg     eax                                     ;change result sign as Mouse signature
   ;
end_main:
        mov     ebx, eax                        ;pass result value to EBX
        invoke  SetConsoleMode, hInput, lpMode  ;recover original console mode
        invoke  ExitProcess, ebx                ;and return result value

Main    ENDP

        end     Main


Antonio
Last edited by Aacini on 04 Feb 2014 19:18, edited 3 times in total.

carlos
Expert
Posts: 488
Joined: 20 Aug 2010 13:57
Location: Chile
Contact:

Re: modern reply.exe created using only batch

#6 Post by carlos » 31 Jan 2014 11:35

@Aacini: thanks for the program and for post the source. I like it so much, it can be useful for batch games. Please you can do a little fix?: disable quick edit mode, when cmd use it any clic select a cell an is not considered as clic. About the code for generate the binary, i like because it read the hex data directly from the batch script, as you name it: a resource. Also, I like much the rle encoding, that in combination with cabinet file, can reduce the hex data length.

Edit: Also, the sum maybe is 256, not 255, 0+255 = 255. the 255 is a normal ascii character. Or I'm confused?
Edit 2: Also, please you can restore the console mode: I remember do this in c (pseudocode):

Code: Select all

old_mode = GetConsoleMode;
new_mode = (old_mode | enable_mouse_input) & ~(enable_quick_edit_mode);
SetConsoleMode(new_mode);
//todo: get the mouse input
//restore mode:
SetConsoleMode(old_mode);

h.k
Posts: 2
Joined: 30 Jan 2014 04:39

Re: modern reply.exe created using only batch

#7 Post by h.k » 31 Jan 2014 14:09

Code: Select all


carlos wrote:

> I calculate the length with a function that not use a goto loop,
> because goto loop are slower.

If you want fast code, don't use batch code. For me, the most
important part of batch programming is readability. Reducing
readability just to save a few milliseconds in a part of the
code, which doesn't matter in the overall execution time, isn't
a good idea.


> And this post is for celebrate the technique of create binary data
> using only batch, not certutil.

Why is it "batch only" when you use "makecab", "findstr" and "expand"
but not if you use "certutil"?


> Certutil have the disadvantage
> that is not present on windows xp.

And even "for /f" is not present on Win98. Backward compatibility
always is limited. There are no security updates for XP in a few
month, so why care about XP? On the other side, most XP installations
are 32 bit versions for which there is a true "batch only" solution
backward compatible to DOS 1.0.



> Also I reduce the size of the generated hex data using a cabinet
> file. Please compare this: The hex data of your swap.exe in base
> 64 are 1412 bytes.

This is including the <cr><lf> after 64 hex characters.


> The same swap.exe compressed as mszip in a
> cabinet produce a hex data with 806 bytes.

Add 12 bytes (call :w ""<cr><lf>) for each line with 60 hex characters:

806 + 806/60*12 = 974 byte

then add 963 byte for the decoding code and you get an overall of
974+963 = 1937 byte


> 1412-806 = 606 bytes saved.

1412 - 1937 = -525


carlos
Expert
Posts: 488
Joined: 20 Aug 2010 13:57
Location: Chile
Contact:

Re: modern reply.exe created using only batch

#8 Post by carlos » 31 Jan 2014 15:59

@hk: If you consider batch slower, why you add more slower time using the goto system? You understand how works the goto system?. In programming the readibility is important, also called code style. But if the result need a optimization performance, then use a tested function that is knowed that works, that is speedy, you can keep readibility adding a comment saying what do that function. In this case is important because the :w function is called many times.
I considered xp present in the market, also win98 is present in a less less less %, but winxp have the same cmd nt batch syntax. Because it, the compatibility with xp is important.
With only batch I referring to not create a certutil script, because in your case the certutil script is the batch script self (a hibrid script), it will not work for include other binary, because the data is considered only from the first -----BEGIN CERTIFICATE----- to the first -----END CERTIFICATE-----
Anyways, your solution is valid in some scenarios (not winxp). But for avoid the limitations that have certutil we developed this method.
Note: this topic was for celebrate this technique and show how use it, not for comparations with other methods.

Aacini
Expert
Posts: 1605
Joined: 06 Dec 2011 22:15
Location: México City, México

Re: modern reply.exe created using only batch

#9 Post by Aacini » 01 Feb 2014 00:13

carlos wrote:@Aacini: thanks for the program and for post the source. I like it so much, it can be useful for batch games. Please you can do a little fix?: disable quick edit mode, when cmd use it any clic select a cell an is not considered as clic. About the code for generate the binary, i like because it read the hex data directly from the batch script, as you name it: a resource. Also, I like much the rle encoding, that in combination with cabinet file, can reduce the hex data length.

Edit: Also, the sum maybe is 256, not 255, 0+255 = 255. the 255 is a normal ascii character. Or I'm confused?
Edit 2: Also, please you can restore the console mode: I remember do this in c (pseudocode):

Code: Select all

old_mode = GetConsoleMode;
new_mode = (old_mode | enable_mouse_input) & ~(enable_quick_edit_mode);
SetConsoleMode(new_mode);
//todo: get the mouse input
//restore mode:
SetConsoleMode(old_mode);

@carlos: Yes, you are right in all points! In Windows 8 the Quick Edit mode is no longer active by default so the first version worked correctly in my computer, but the original console mode must be preserved at end, of course. :?

I fixed these points and modified the code in the original post. Delete GetInput.exe file and copy the Batch file again. Please, report if this new version fix the previous problems.

Antonio

carlos
Expert
Posts: 488
Joined: 20 Aug 2010 13:57
Location: Chile
Contact:

Re: modern reply.exe created using only batch

#10 Post by carlos » 01 Feb 2014 16:10

@Aacini: thanks. It Works ok.

Aacini
Expert
Posts: 1605
Joined: 06 Dec 2011 22:15
Location: México City, México

Re: modern reply.exe created using only batch

#11 Post by Aacini » 01 Feb 2014 22:09

I slightly modified GetInput.exe program to also report right-button mouse clicks and differentiate they from left-button clicks incrementing the column position by 32768. I also modified the example Batch program in order to achieve a more precise identification of the key pressed. The new code is in the same place of the original one, as usual; remember that you must delete GetInput.exe file from disk before run the new Batch program. This is an output screen example using the new code:

Image

I learned from previous screen that Alt-Gr (Alt-gray or right-side Alt) key is the same than a Ctrl-Alt combination. My computer have Latin America keyboard layout and requires Alt-Gr key to enter \~@^` characters, but after seen in the screen the result of Alt-Gr key, I tried with Ctrl-Alt combination instead, and it works!

Antonio

[moderator]: I read a motion about nominating carlos for expert level. I second that motion.

einstein1969
Expert
Posts: 763
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: modern reply.exe created using only batch

#12 Post by einstein1969 » 03 Feb 2014 05:29

carlos wrote:.. Also, I like much the rle encoding, that in combination with cabinet file, can reduce the hex data length.



why rle encoding? Using base encoding is much better

einstein1969

foxidrive
Expert
Posts: 6033
Joined: 10 Feb 2012 02:20

Re: modern reply.exe created using only batch

#13 Post by foxidrive » 03 Feb 2014 06:08

einstein1969 wrote:why rle encoding? Using base encoding is much better

einstein1969


Why is it better? Does it compress recurring bytes also?

einstein1969
Expert
Posts: 763
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: modern reply.exe created using only batch

#14 Post by einstein1969 » 03 Feb 2014 06:24

foxidrive wrote:
einstein1969 wrote:why rle encoding? Using base encoding is much better

einstein1969


Why is it better? Does it compress recurring bytes also?


I'm sorry, I meant that a file already compressed (carlos uses makecab normally) if passed to another base (higher base is better) has less wasted space than using a low base + RLE
When moving from one base to another file already compressed, bytes is unlikely that there are recurring.
You can of course combine the change of base + RLE but you will not have big advantages if base is HIGH.
Aacini uses RLE because it uses a low base i think. Or not use other compression.

In my thread I say that is a waste of space by 13% but I do not remember what basis I were referring to.
This means that a file of 500bytes occupies the end less than 600bytes format BAT / FORUM COMPATIBLE

EDIT: Aacini seems to use base 16 , maybe base 17-18 with [] used for RLE rarely.

einstein1969

carlos
Expert
Posts: 488
Joined: 20 Aug 2010 13:57
Location: Chile
Contact:

Re: modern reply.exe created using only batch

#15 Post by carlos » 04 Feb 2014 00:23

@einstein1969: the base used is 16 with rle that uses [number] that means repetition of characters.

Post Reply