Pulling my hair out.. simple stringlen counter breaks script

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
Magialisk
Posts: 104
Joined: 25 Jul 2013 19:00

Re: Pulling my hair out.. simple stringlen counter breaks sc

#31 Post by Magialisk » 07 Aug 2013 04:31

Not that I'm trying to pressure you or anything, but I whipped up a quick example to show you how to store and retrieve data from an ADS within the host batch file. A FOR loop can extract the data just fine, as shown below in file Testing.bat:

Code: Select all

@echo off

::Init ADS filename
set "thisbat=%~dpf0"
set "ADS=%thisbat%:TestADS"

::create ADS & show contents
echo this is a test > %ADS%
more < %ADS%

:Retrieve ADS
FOR /F "delims=." %%a IN (%ADS%) DO set data=%%a

:Display results
echo.
echo ADS data=%data%

And here were the results:

Code: Select all

C:\Users\Marc\Desktop>testing
this is a test

ADS data=this is a test

C:\Users\Marc\Desktop>

For the most part you can do anything with an ADS that you would do with a normal file, in fact when you access a normal file in NTFS you're implicitly accessing the ":data" data stream, because it's the default when no "alternate" data stream is given. You're right that not all shell commands understand the ADS syntax with the colon, so don't try to get too crazy complicated, but just storing a variable value and retrieving it later is simple, and avoids the clutter of temp files or registry keys.

This is generally advertised as a malicious technique, used to store executable content or batch files in otherwise inconspicuous files, and later run said content with "start" or "call". I've been itching to put it to use for something practical, but my own scripts go on and off USB sticks too often which would strip the ADS...

MKANET
Posts: 160
Joined: 31 Mar 2012 21:31

Re: Pulling my hair out.. simple stringlen counter breaks sc

#32 Post by MKANET » 08 Aug 2013 10:10

Okay,

I "think" I was successful in converting my GeoIP to a full macro with arguments. I'm not sure if it's the cleanest way to do it. I just used the existing stringlength counter as a reference. If you guys see any way to make it even cleaner/more efficient, please let me know.

Also, I found a practical way to implement ADS. I attached the data file to stunnelx.log instead of the executable. So, all I need to do is delete stunnelx.log in Windows Explorer to reset everything; instead of it being a hassle to delete the invisible file separately using command-line.

Code: Select all

@echo off
setlocal
call :GeoIP
call :getStringLength

setlocal enableDelayedExpansion
set "log=\stunnel.log"
set "logx=\stunnelx.log"
set "linecount.tmp=%logx%:linecount.tmp"
set "location.length="
set "padding=                                                                                                                                 "
set "linecounter="
set "SKIP="

if exist "!logx!" (
   for /f "delims=" %%a in ('more ^< "!linecount.tmp!"') do set SKIP="skip=%%a tokens=1-3* delims= "
   ) else (
   type NUL > "!logx!"
   echo 0 >"!linecount.tmp!"
   set SKIP="tokens=1-3* delims= "
   )

for /L %%a in ( 0, 1, 9) do set "meridiemness[0%%a]=am"
for /L %%a in ( 0, 1,11) do set "meridiemness[%%a]=am"
for /L %%a in (12, 1,23) do set "meridiemness[%%a]=pm"
for /L %%a in (0, 1, 23) do (
   set /A "num=((11 + %%a)%%12)+101"
   set "hour[!num:~-2!]=!num:~-2!"
   set "hour[%%a]=!num:~-2!"
)
set "hour[00]=12"

(
   for /f %SKIP% %%a in ('findstr /V /I /C:"127.0.0" /C:"192.168" /C:"byte" /C:"WSAECONNRESET" /C:"Peer suddenly disconnected" "%log%"') do (
      set "dateValue=%%a"
      set "dateValue=!dateValue:~5,2!/!dateValue:~8,2!/!dateValue:~2,2!"

      for /F "tokens=1* delims=:" %%A in ("%%b") do set "timeValue=!hour[%%A]!:%%B!meridiemness[%%A]!"

      set "info=%%d"

      set "ipPort="
      for %%A in (!info:~24!) do set "ipPort=%%A"
      for /F "tokens=1 delims=0123456789.:" %%e in ("!ipPort!") do set "ipPort="
      for /F "tokens=1,2 delims=:" %%A in ("!ipPort!") do (
         if not defined DN[%%A] (
            for /f "tokens=2 delims= " %%0 in ('nslookup %%A 2^> nul ^| findstr /I /C:"Name" ') do set "DN[%%A]= %%0 "
            if not defined DN[%%A] set "DN[%%A]= "
         )
         set "info=!info: %%A:%%B=!:!DN[%%A]![%%A]"
      set IP=%%A
      %$GeoIP% "location" "IP"
      )
      set "info=!dateValue! !timeValue! - !info!"

      ::::::::::::::::::::: Padding Code :::::::::::::::::::::
      set "print=!info!"
      %$strlen% "print.length" "print"
      set /A "availablePaddingChars=129-!print.length!"
      if 0 LSS !availablePaddingChars! for %%A in (!availablePaddingChars!) do set "print=!info!!padding:~-%%A!!location!"

      echo(!print!
     set /a linecounter=linecounter+1
   )
) >> "%logx%"
set /a N=N+linecounter
echo(!N!>"!linecount.tmp!"

endlocal
endlocal


GOTO:EOF
:GeoIP
set LF=^


::Above 2 blank lines are required - do not remove
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"

set argv=Empty
set $GeoIP=for /L %%n in (1 1 2) do ( %\n%
   if %%n==2 (%\n%
      for /F "tokens=1,2 delims=, " %%1 in ("!argv!") do (%\n%
         set "IP=!%%~2!"%\n%
       set "location="%\n%
         set "country="%\n%
         set "state="%\n%
         set "city="%\n%
         for /F "tokens=*" %%a in ('curl -s freegeoip.net/json/!IP!') do set geoIP=%%a%\n%
         for /F "tokens=1-6 delims=," %%a in ("!geoIP!") do (%\n%
            set "country=%%c"%\n%
            set "state=%%e"%\n%
            set "city=%%f"%\n%
            )%\n%
         for /F "tokens=1-2 delims=:" %%a in ("!country!") do set "country= ◄ %%~b ►"%\n%
         for /F "tokens=1-2 delims=:" %%a in ("!state!") do set "state= %%~b"%\n%
         for /F "tokens=1-2 delims=:" %%a in ("!city!") do set "city= %%~b,"%\n%
         if "!city!" == " ," set "city="%\n%
         if "!state!" == " " set "state="%\n%
         if "!country!" == " ||" set "country="%\n%
         if not [!city!!state!!country!] == [] set "location=!country! !city!!state!"%\n%
         set "%%~1=!location!"%\n%
      ) %\n%
   ) %\n%
) ^& set argv=,
exit /b

:getStringLength
set LF=^


::Above 2 blank lines are required - do not remove
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"

:::: StrLen pResult pString
set $strLen=for /L %%n in (1 1 2) do if %%n==2 (%\n%
   for /F "tokens=1,2 delims=, " %%1 in ("!argv!") do (%\n%
      set "str=A!%%~2!"%\n%
      set "len=0"%\n%
      for /l %%A in (12,-1,0) do (%\n%
         set /a "len|=1<<%%A"%\n%
         for %%B in (!len!) do if "!str:~%%B,1!"=="" set /a "len&=~1<<%%A"%\n%
      )%\n%
      for %%v in (!len!) do endlocal^&if "%%~b" neq "" (set "%%~1=%%v") else echo %%v%\n%
   ) %\n%
) ELSE setlocal enableDelayedExpansion ^& set argv=,
exit /b

Magialisk
Posts: 104
Joined: 25 Jul 2013 19:00

Re: Pulling my hair out.. simple stringlen counter breaks sc

#33 Post by Magialisk » 08 Aug 2013 19:49

That sounds like a good idea to attach an ADS to the output log. I'm glad you found a good way to implement it. You shouldn't really have to worry too much about deleting the ADS anyway since it doesn't make the target file any larger (the ADS data is stored elsewhere in the filesystem metadata), but I do like your implementation.

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

Re: Pulling my hair out.. simple stringlen counter breaks sc

#34 Post by foxidrive » 09 Aug 2013 04:01

There have been discussions about ADS here and there in the past - I seem to recall that ADS data gets orphaned if it is not deleted in a certain way, and needs to be cleaned.

I don't recall specifics because I found the issues to be more effort that it was worth and very unreliable, in terms of copying and disk backup, so I haven't investigated the process much.
Just thought I'd mention my, possibly incorrect, recollections.

Magialisk
Posts: 104
Joined: 25 Jul 2013 19:00

Re: Pulling my hair out.. simple stringlen counter breaks sc

#35 Post by Magialisk » 09 Aug 2013 04:41

There can definitely be issues. I don't use ADS because my files go on and off USB sticks regularly and ADS gets stripped if the file leaves an NTFS file system. As long as it stays on an NTFS file system though, the ADS is there to stay. It will even tag along though a CIFS share, etc. and stay with the file.

The biggest pain in the butt is that you can't really delete an ADS without deleting the file (or copying it to a USB stick :)). This is probably what you meant by cleaning up? The most common method I see to remove an ADS is to rename the file, type its contents into a new file of the original name, then delete the renamed version (and along with it the attached ADS). I agree that's cumbersome, but I only suggested this method since the OP was looking for an easy way to store a line counter somewhere, and a temporary file seemed wasteful to me.

I figured storing 3-5 characters of text in an ADS wasn't going to be a big deal even if he never bothered to clean it. If he were trying to store an entire "daughter script" or function call, etc. I'd not have recommended this approach. The registry would be another reasonable place to store his data, if he was willing to play around in there. There's always multiple ways to solve the problem.

Post Reply