Delete random services with number name script (ex. 10009752) - batch-file

I work for a IT consulting company and one of our clients got hit with a virus that created a bunch of random exe files on the PC and then it created services to try to run those files automatically. I've since removed the virus from all machines, but there are a lot of orphaned services left I need to remove.
All in all there are 4,000 unique services combined from all of the computers at their facility. The services are random numbers and I am hoping to find a way to remove them with a script of some sort, but I can't figure out how to do a wildcard for the commands I know....
I've tried
reg delete hklm\system\currentcontrolset\services\1*
and
sc delete \\pcname 1*
without any luck. Any help would be appreciated so I don't have to go to each machine (again) and manually delete the services. Sorry I am always learning new scripts by going out and seeing what other people have created, but I can't find anything to even build off of for this situation.
Thank you in advance.

We can imagine a script that get all services names and check if the name is numeric, if so delete the service :
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
for /f "tokens=1 delims= " %%a in ('tasklist') do (
set "$service=%%a"
echo !$service:.exe=! | findstr "^[-][1-9][0-9]*$ ^[1-9][0-9]*$ ^0$">nul && call:delete %%a || echo %%a is not numeric
)
exit/b
:delete
echo %1 is Numeric
echo here the command to delete the service (%1)echo off
Put the command to remove the service in the :delete label
Using #Endoro's solution to check if a value is Numeric :

#echo off
setlocal
set "root_key=HKLM\system\currentcontrolset\services"
for /f "tokens=5 delims=\" %%A in ('reg query "%root_key%"') do (
call :check_key "%%~A"
if errorlevel 1 call :del_key "%%~A"
)
goto :eof
:check_key
setlocal enabledelayedexpansion
set "filtered="
set "original=%~1"
if not defined original exit /b 0
for /f "delims=0123456789" %%A in ("!original!") do set "filtered=%%~A"
if not defined filtered exit /b 1
exit /b 0
:del_key
echo "%~1"
rem sc stop "%~1"
rem sc delete "%~1"
goto :eof
Use of reg query to loop through the services key. The 5th token
delimited after \ is the key name of the service.
call :check_key with the for loop delimits on digits 0 to 9. If
filtered is not defined, then only digits existed in the key name
so it will exit the label setting errorlevel 1.
If errorlevel 1 is caught in the main for loop, then call :del_key
runs sc stop to stop the service and sc delete deletes the service
key. Remove the rem before the sc commands to enable them.
Created for local machine use though some minor changes could do remote use.

You can use a VbScript for that, simple, first instance the WMI service:
Computer = "." 'IP or Network Name ("." is Local host)
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & Computer & "\root\cimv2") 'If your user have the access
or.. if you need use other user.
Computer = "." 'IP or Network Name
UserAndDomain = ".\Administrator" 'Account WITH domain "\"
Pass = "" ' Account Password
Set objSWbemLocator = CreateObject("WbemScripting.SWbemLocator")
Set objWMIService = objSWbemLocator.ConnectServer(Computer, "Root\CIMv2", UserAndDomain , Pass)
Now obtain the Services Collection:
Set colServices = objWMIService.ExecQuery("Select * from Win32_Service") ' Where Name ='" & strServiceName & "'" or "Like = 'Someting%'"
'Or
Set colServices = objSWbemServices.InstancesOf("Win32_Service")
And then to delete:
For Each Service in colServices
If IsNumeric(Service.Name) Then 'Or other condition.
Service.Delete
End If
Next

Related

How to fix appending a timestamp when moving a folder on a remote server to a new location on the remote server?

I am trying to run a batch script on my local machine that will take care of some log archiving on some servers. I can access the servers via file explorer "\SERVERNAME\C$\SOME FOLDER." When I attempt to xcopy from the source to the destination locally and append a timestamp its like the TIMESTAMP variable doesn't store my date/time concatenation.
This is for windows 2012r2 servers, I've tried to append just the date\time to the end which works fine, however, its not the desired format I am looking for and it starts nesting the directory with the date but no time and it looks like a mess. :(
I've also tried to use the wmic however this is the first time I am writing a batch file to automate some tasks so all this has been a great learning experience.
I've tried to echo %TIMESTAMP% and nothing returns? I've even tried to add the concatenation (%CUR_YYYY%%CUR_MM%%CUR_DD%-%CUR_HH%%CUR_NN%%CUR_SS%) directly to the file directory and its doesn't work :(
REM Check to see if a service on the machine is stopped (it is always stopped by the time it gets here) before we move the files from the logging directory to a new one.
for /F "tokens=3 delims=: " %%H in ('sc \\REMOTESERVER query "SOME SERVICE NAME" ^| findstr " STATE"') do (
if /I "%%H" == "STOPPED" (
REM substring the date and time and then concat it together at the end to make the desired timestamp variable
set CUR_YYYY = %date:~10,4%
set CUR_MM = %date:~4,2%
set CUR_DD = %date:~7,2%
set CUR_HH = %time:~0,2%
set CUR_NN = %time:~3,2%
set CUR_SS = %time:~6,2%
set CUR_MS = %time:~9,2%
set TIMESTAMP = %CUR_YYYY%%CUR_MM%%CUR_DD%-%CUR_HH%%CUR_NN%%CUR_SS%
REM copy files from the servers source directory and then move the files to a newly created logging folder with a timestamp appened at the end
echo d | xcopy /f /y "\\REMOTE SERVER\src" "\\REMOTE SERVER\dest\Logging_%TIMESTAMP%" /E /I
REM delete the contents of the servers source directory to keep things nice and clean
pushd \\REMOTE SERVER\src && del . /F /Q popd
)
)
The expected result would look like:
SourceFolder on the server will be there but empty
DestinationFolder will have a new Logging folder created Logging_20190325010101 and within the newly created logging folder all the contents from the SourceFolder should be there.
You need to get rid of the whitespace before and after your = in your set commands, also, You need delayedexpansion in the codeblock with changing variables, and there is a better way to get rid of the colons and comma.
#echo off
setlocal enabledelayedexpansion
REM Check to see if a service on the machine is stopped (it is always stopped by the time it gets here) before we move the files from the logging directory to a new one.
for /F "tokens=3 delims=: " %%H in ('sc \\REMOTESERVER query "SOME SERVICE NAME" ^| findstr " STATE"') do (
if /I "%%H" == "STOPPED" (
REM substring the date and time and then concat it together at the end to make the desired timestamp variable
set "CUR_YYYY=%date:~10,4%"
set "CUR_MM=%date:~4,2%"
set "CUR_DD=%date:~7,2%"
set "mytime=!time::=!"
set "mytime=!mytime:,=!"
set "TIMESTAMP=!CUR_YYYY!!CUR_MM!!CUR_DD!-!mytime!"
REM copy files from the servers source directory and then move the files to a newly created logging folder with a timestamp appened at the end
echo d | xcopy /f /y "\\REMOTE SERVER\src" "\\REMOTE SERVER\dest\Logging_!TIMESTAMP!" /E /I
REM delete the contents of the servers source directory to keep things nice and clean
pushd \\REMOTE SERVER\src && del . /F /Q popd
)
)
To explain your issue however, when you set a variable, the whitespace comes as part of the variable.. So:
set variable = value
Will result in a variable with a trailing space %variable % and a value with a leading space <space>value So we always get rid of the whitespace and best to use double quotes to eliminate possible whitespace after the value. for instance:
set "variable=value"
which will create %variable% and value
Within parenthetical code blocks you have to delay expansion when retrieving variable values in the same block in which they were set. For example:
(
set "test=1"
echo [%test%]
)
... would echo "[]" because %test% was retrieved within the same parenthetical code block in which it was set. At the time %test% is evaluated, it has no value. You must delay expansion either by using setlocal enabledelayedexpansion or by using call echo [%%test%%] (or cmd /c or similar). When using setlocal enabledelayedexpansion, you delay expansion by using ! instead of % to denote variables. See setlocal /? and set /? in a cmd console for more information -- particularly the section of set /? that begins "Finally, support for delayed environment variable expansion has been added."
Also, it's much simpler and more locale agnostic to compose your timestamp using wmic os get localdatetime. Example:
for /f "delims=." %%I in ('wmic os get localdatetime /value ^| find "="') do set "%%~I"
echo %localdatetime%
That should result in %localdatetime% containing the current numeric value of YYYYMMDDHHMMSS.

Looking for assistance with batch file logic whether to determine whether a user has logged into AD or not

In attempting a proof of concept, we a script run by our desk for the creation of user home directories, group membership, profile, set home directory, etc.
I'd like to add a section that checks whether the account has the lastLogonTimeStamp (LLTS) value set in AD. I'm able query the user in AD. What I'm unable to figure out is how to parse out and run separate steps in the .cmd file whether there's a value in the LLTS attribute vs no value. I don't need to look at the age, just whether it's there or not. I believe my issue is that I'm getting a string returned when I'm trying to compare a value, and if the LLTS isn't present, I'm not sure if it's a zero value or a null value.
I understand there may be better ways to do this using PS or VBS, but we're not at that point of converting our scripts, but we'd like to try an figure out this piece.
Here's what I have so far as a POC / test.
#echo off
:START
cls
echo Enter EMP ID
set /p EMPID=
SetLocal EnableDelayedExpansion
set count=0
:: queries for the existence of the lastLogonTimeStamp value
for /F "delims=" %%a in ('dsquery * domainroot -filter "(&(objectCategory=Person)(objectClass=User)(samaccountname=%empid%))" -attr lastLogonTimeStamp') do (
set LLTS=%%a
set /a count=!count! + 1
if %%a GTR 1 GOTO COMPARE
)
:COMPARE
echo.
echo OLD-LLTS=%LLTS%
set MODLLTS=%LLTS: =%
echo MOD-LLTS=%MODLLTS%
IF %MODLLTS% GTR 1 GOTO HAS
:HASNOT
:: Run these commands if the lastLogonTimeStamp is not present
ECHO Has NO previous Logon info.
pause
GOTO END
:HAS
:: Run these commands if the lastLogonTimeStamp is present, regardless of value.
ECHO Has previous Logon info.
w32tm.exe /ntte %MODLLTS%
pause
GOTO END
:END
EXIT
Here's example output with a user has previously logged in:
administrator
lastLogonTimeStamp
131545455582093968
Here's example output with a user who's never logged in:
guest
lastLogonTimeStamp
Note:There's an empty linebelow the attribute name for the guest that's not showing in the preview.
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\q47309403.txt"
SET "filename2=%sourcedir%\q47309403_2.txt"
FOR %%a IN (logonname logonjunktext logontimestamp) DO SET "%%a="
FOR /f "usebackqdelims= " %%a IN ("%filename1%") DO (
IF DEFINED logonname (
IF DEFINED logonjunktext (
SET "logontimestamp=%%a"
) ELSE (
SET "logonjunktext=%%a"
)
) ELSE (
SET "logonname=%%a"
)
)
:done1
ECHO name=%logonname% junk text=%logonjunktext% timestamp=%logontimestamp%
FOR %%a IN (logonname logonjunktext logontimestamp) DO SET "%%a="
FOR /f "usebackqdelims= " %%a IN ("%filename2%") DO (
IF DEFINED logonname (
IF DEFINED logonjunktext (
SET "logontimestamp=%%a"
) ELSE (
SET "logonjunktext=%%a"
)
) ELSE (
SET "logonname=%%a"
)
)
:done2
ECHO name=%logonname% junk text=%logonjunktext% timestamp=%logontimestamp%
GOTO :EOF
I used files named q47309403.txt, q47309403_2.txt containing your data for my testing. Obviously, replace such with your dsquery command.
The code is simply the same, repeated for each file for demo purposes.
The data reported in the variables should enable you to derive any consequential actions, if your report on the dsquery output is representative.
Operation is simply clear three variables, then accumulate each depending on the defined state for the variable.

stopping / starting services on an remote computer with wait for the result

I am writing a batch script which should stopp services on a remote computer, copy something and then start it again.
The fact, that sc doesn't wait for the service to return a full stop / start signal doesn't satisfy me, as I fear that the service might be inconsistend or failing, and then could damage the program code / database which is depending on those services.
therefore I searched for a workaround, to have something similiar to usage of net , and come around this:
sc \\remote_server stop Service1 >> %LOGFILE%
:askservice1
for /f "tokens=4" %%a in (' sc \\remote_server query Service ^|findstr STATE') do set ServiceResult=%%a
if %ServiceResult%=STOPPED (goto nextservice2)
goto askservice1
:nextservice2
sc \\remote_service stop Service2 >> %LOGFILE%
:askservice2
for /f "tokens=4" %%a in (' sc \\remote server query Service ^|findstr STATE') do set ServiceResult2=%%a
if %ServiceResult2%=STOPPED (goto nextservice3)
goto askservice2
this goes on for 6 services, then the copy will be done, and then the run should go other way round with starting up
as you can see, this is a. really strange and looks confusing and b, it could end in an endless loop if the service won't get to the state I am comparing to...
my questions would be, how can I terminate the goto after a few tries and just let it go to the next service ?
or do you have any other code for me that helps ? I am limited to use batch or powershell but as I've never used PS before, I couldn't understand the solutions I've found.
Try this and see if it works for you. I tested it on my local machine with 2 services and it worked well. You'll have to tweak some of the ping timeout settings and take out the echo for the file copy but overall it should give you a nice starting place.
#echo off
setlocal enabledelayedexpansion
set "stopstart=stop"
set "state=STOPPED"
set "remoteserver=REMSERV"
set "LogFile=Logfile.log"
if exist %LogFile% del /q %LogFile%
:Start
for %%S in (service1 service2 service3 service4 service5 service6) do (
sc \\%remoteserver% %stopstart% %%S>>%LogFile%
Call :WaitForService %remoteserver% %%S %state% ret && (
echo Service %%S %state%
set /a svc+=1) || (
Service %%S is !state!
)
)
if "%svc%" EQU "6" (
echo copy file now.
ping -n 10 127.0.0.1>nul
set "stopstart=start"
set "state=RUNNING"
goto :Start
)
if "%svc%" EQU "12" (Echo All services are now running.)
exit /b
:WaitForService <remoteserver> <service> <stopstart> <return>
setlocal
for /L %%a in (1,10,1) do (
for /f "tokens=4" %%a in ('
sc \\%~1 query %~2^|findstr "STATE"') do (
ping -n 2 -w 10000 127.0.0.1>nul
if "%%a" EQU "%~3" set %~4=%%a & exit /b 0
)
exit /b 1
)
What it's doing is setting some variables at the start then looping through all 6 of your services sending them to a subroutine that checks the wait state given to it. In the first iteration, it's STOPPED so it checks in a loop for the service to be stopped. Once it is STOPPED, it sends an exit code and we check that exit code to make sure the service stopped. At this point, we add a +1 to a variable to keep track of each service that has stopped. Once all 6 have stopped, we copy the file in question, flip the state of the services to RUNNING and run through the routine again waiting for each of the services to start. Once all of them are started, it Echo's all services are running and ends. You can probably expand upon it more by checking additional states and running through the WaitForService routine until everything has STOPPED if you need to but in my testing, the services just stopped and started without any hiccups.

Change password for all users accounts in command prompt

Alright, I need something of a batch scripting guru to help me out of the corner that I've backed myself into.
I have a program that runs as system and I want to change the password for all the accounts that appear in the output for net user. I'm not really sure how I could do this with just command line or AHK-based scripting.
When I perform a net user command the output is like this for me:
C:\Users\Resident>net user
User accounts for \\9100BASELINE
-------------------------------------------------------------------------------
Administrator Guest Resident
The command completed successfully.
I need some way to change the password on all the accounts here (be it 3 or 50) to something of my choosing.
Can anyone help me out with this? I tried slapping together a for loop where each item is a token before I realized that I don't know how to regex the usernames out of there.
You can actually do this purely with batch.
The simplest method would be to list all the folders with:
cd C:\Users
dir /b /o:n /ad > users.txt
After that, you'd use a for loop that sets a password to each user in the Users directory, and makes sure that the script doesn't try to set a password for the Public folder, as it is not a user.
It'd look like this:
for /f %%i in ('type C:\Users\users.txt') do(
if not %%i==Public (
net user %%i [Insert Password Here]
)
)
Technically, you don't actually have to check for extra non user folders, as cmd won't actually cause any problems if it attempts to set a password for a non user folder.
I'd recommend employing the help of a little VBScript:
Set accounts = GetObject("WinNT://.")
accounts.Filter = Array("user")
For Each user In accounts
WScript.Echo user.Name
Next
Save it as listusers.vbs and run it like this:
#echo off
setlocal
set /p "newpw=Enter new password: "
for /f "delims=" %%u in ('cscript //NoLogo C:\path\to\listusers.vbs') do (
net user "%%u" "%newpw%"
)
Edit: If you want to omit specific accounts from being processed you can either add an exclude list to the VBScript:
Set exclude = CreateObject("Scripting.Dictionary")
exclude.CompareMode = vbTextCompare
exclude.Add "HomeGroupUser$", True
exclude.Add "otheruser", True
...
Set accounts = GetObject("WinNT://.")
accounts.Filter = Array("user")
For Each user In accounts
If Not exclude.Exists(user.Name) Then WScript.Echo user.Name
Next
or filter the output of listusers.vbs with findstr:
for /f "delims=" %%u in (
'cscript //NoLogo C:\path\to\listusers.vbs ^| findstr /v /i ^
/c:HomeGroupUser$ /c:otheruser ...'
) do (
net user "%%u" "%newpw%"
)

batch file goto next xcopy command if windows "Admin" username has changed

I want to be able to run just one instance of xcopy rather than many, from a usb drive to .\admin\desktop on the computer I have plugged it in, however there might be some computers I get on that have the admin username changed to the name of the person. Is there a generic batch namimg convention for the admin user account for windows? If so I'd like to just use that whatever it may be instead of listing everyone's username for every computer and guessing what it might be without looking everytime.
Here is what I have so far, it works well if I know for a fact that the "Admin" user is still labeled "Admin."
#echo off
xcopy "%~dp0M1k_SWPCB\*.*" "C:\Documents and Settings\Admin\Desktop\SWPCB\" /d /s /h /v /c /f /k /y
pause
I tried 'All Users' as well, but in some cases the directory doesn't exist and it will not work. Plus if the computer has multiple users I don't want it on everyones' desktop.
Any help would be much appreciated.
Thanks
All users have a SID identifier and the local admin account always ends with the -500 suffix, so you can get the Admin username by checking the SID's on the Registry:
#Echo OFF
FOR /F "Tokens=*" %%# IN ('Reg Query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" ^| FIND "-500"') DO (
FOR /F "Tokens=2,*" %%A IN ('Reg Query "%%#" /v "ProfileImagePath" ^| FIND /V "%%#"') DO (
Echo Admin SID: %%~n#
Echo Admin Folder: %%B
)
)
Pause>NUL&Exit
Output:
Admin SID: S-1-5-21-148789306-3749789949-2179752015-500
Admin Folder: C:\Users\Administrador
Another way to do it is with an VBScript, you can use it in your Batch file and write the Admin name to a textfile, then next you will set a variable with the content of the textfile. (I don't wrote this function):
Set objNetwork = CreateObject("Wscript.Network")
objComputerName = objNetwork.ComputerName
Set objwmi = GetObject("winmgmts:{impersonationLevel=impersonate}!//" & objComputerName)
qry = "SELECT * FROM Win32_Account where Domain = '" & cstr(objComputerName) & "'"
For Each Admin In objwmi.ExecQuery(qry)
If (Left(Admin.sid, 6) = "S-1-5-" And Right(Admin.sid,4) = "-500") Then MsgBox Admin.name)
Next
PS: Maybe someone will post other solution saying that listing the group names is another choice... but groupnames is not a generic solution 'cause the native language.

Resources