Q:

Batch - Decreasing value of a variable in substring is not functioning

I'm trying to reverse "hello" to "olleh". But the output shows "ooooo".
I think !string:~%back%,1! is the problem, because when I use echo to test the value of back is decreasing or not, it works, but it doesn't work in substring, so it always get the last character of the string (-1,1).
@echo off

set string=hello
set temp_string=%string%
set /a string_length=0

:find_length
if defined temp_string (
    set temp_string=%temp_string:~1%
    set /a string_length+=1
    goto :find_length
)

:loop
setlocal enabledelayedexpansion

set /a back=-1

for /l %%a in (1,1,!string_length!) do (
    set reverse_string=!string:~%back%,1!!reverse_string!
    set /a back-=1
)

echo !reverse_string!
pause >nul

A:

As TripeHound commented, %back% needs to be delayed.  What you should do is use the for /L loop value of %%a to in place of %back%.  (No sense decrementing a variable manually when one's already being decremented for you as a part of the for /L loop, right?)
for /l %%a in (%string_length%,-1,0) do (
    call set "reverse_string=!reverse_string!!string:~%%a,1!"
)

goto loops are not very efficient.  If you've got a long string you're going to reverse, there'll be a noticeable pause while you count its length if you goto :label for each character.  The fastest way I've found to get the length of a string is based on jeb's answer here:
:length <return_var> <string>
setlocal enabledelayedexpansion
if "%~2"=="" (set ret=0) else set ret=1
set "tmpstr=%~2"
for %%I in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
    if not "!tmpstr:~%%I,1!"=="" (
        set /a ret += %%I
        set "tmpstr=!tmpstr:~%%I!"
    )
)
endlocal & set "%~1=%ret%"
goto :EOF

Put it all together like this:
@echo off
setlocal

set "string=%*"

call :length string_length "%string%"

setlocal enabledelayedexpansion

for /l %%a in (%string_length%,-1,0) do (
    set "reverse_string=!reverse_string!!string:~%%a,1!"
)

echo(!reverse_string!
pause >nul

exit /b 0

:length <return_var> <string>
setlocal enabledelayedexpansion
if "%~2"=="" (set ret=0) else set ret=1
set "tmpstr=%~2"
for %%I in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
    if not "!tmpstr:~%%I,1!"=="" (
        set /a ret += %%I
        set "tmpstr=!tmpstr:~%%I!"
    )
)
endlocal & set "%~1=%ret%"
goto :EOF

Example output:

command: test.bat The quick brown fox
  result: xof nworb kciuq ehT

