-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathInvoke-GPTObfuscation.psm1
371 lines (303 loc) · 11.4 KB
/
Invoke-GPTObfuscation.psm1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
<#
.Synopsis
Obfuscate PowerShell code using the Invoke-GPTObfuscation function.
.Description
This function will obfuscate PowerShell code using the Invoke-GPTObfuscation function.
.Parameter ScriptCode
The script block to obfuscate.
.Parameter PromptTemplateFile
The prompt template to use. This is a file with a .prompt_tpl extension.
.Parameter PromptTemplate
The prompt template to use. This is a string containing the prompt template.
.Parameter PromptSettings
A hashtable containing variables to use in the prompt template.
.Parameter ShouldSplit
Optional. If true, the script will be split into multiple parts if it is too long to fit into a single prompt. Defaults to false.
.Parameter SplitSize
Optional. The maximum size of each split. Defaults to 1000.
.Parameter AIProviderSettings
A hashtable containing settings for the AI provider.
.Parameter AIProvider
Optional. The AI provider to use. Defaults to "OpenAI".
.Parameter AIProviderKey
Optional. The API key for the AI provider. Defaults to the environment variable OPENAI_API_KEY.
.Parameter Verbose
Optional. If true, verbose output will be displayed. Defaults to true.
.Example
Invoke-GPTObfuscation -ScriptBlock {$str = "Hello World!"; Write-Host $str}
#>
function Invoke-GPTObfuscation {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
[object]
$ScriptBlock,
[Parameter(Mandatory=$false)]
[string]
$PromptTemplateFile = '',
[Parameter(Mandatory=$false)]
[string]
$PromptTemplate = '',
[Parameter(Mandatory=$false)]
[hashtable]
$PromptSettings,
[Parameter(Mandatory=$false)]
[bool]
$ShouldSplit = $false,
[Parameter(Mandatory=$false)]
[int]
$SplitSize = 1000,
[Parameter(Mandatory=$false)]
[hashtable]
$AIProviderSettings,
[Parameter(Mandatory=$false)]
[string]
$AIProvider = 'OpenAI',
[Parameter(Mandatory=$false)]
[string]
$AIProviderKey = ''
)
# Default to verbose output
if($Verbose -eq $null) {
$Verbose = $true
}
if($PromptTemplate -eq $true) {
$PromptTemplate = '' #weird
}
if($PromptTemplateFile -eq $true) {
$PromptTemplateFile = ''#weird
}
# Cannot pass a file and template string at the same time
if(($PromptTemplateFile -ne $null -and $PromptTemplateFile.Length -gt 0) -and ($PromptTemplate -ne $null -and $PromptTemplate.Length -gt 0)) {
throw "Cannot pass a file and template string at the same time (ambiguous): $PromptTemplateFile, $PromptTemplate"
return;
}
elseif($PromptTemplateFile -eq $null -or $PromptTemplateFile.Length -eq 0) {
if($PromptTemplate -eq $null -or $PromptTemplate.Length -eq 0) {
throw "Must pass a file or template string"
return;
}
}
$AIProviderObj = $null
if($AIProvider -eq $null -or $AIProvider -eq '' -or $AIProvider -eq 'OpenAI') { # Default to OpenAI
$AIProviderObj = [OpenAIProvider]::new()
}
else {
# Not implemented yet
throw "AI provider not implemented yet: $AIProvider"
return;
}
# Initialize AI provider
#Set setting for each
if($AIProviderSettings -ne $null) {
foreach($setting in $AIProviderSettings.Keys) {
$AIProviderObj.$setting = $AIProviderSettings[$setting]
}
}
# Set the API key if param is set
if($AIProviderKey -ne $null -and $AIProviderKey -ne "") {
$AIProviderObj.SetSetting("APIKey", $AIProviderKey)
}
if($ScriptBlock -isnot [string] -and $ScriptBlock -isnot [System.Management.Automation.ScriptBlock] -and $ScriptBlock -isnot [array]) {
throw "ScriptBlock must be either a string or System.Management.Automation.ScriptBlock"
return;
}
if ($ScriptBlock -is [System.Management.Automation.ScriptBlock]) {
$script = $ScriptBlock.ToString()
$script = $script.Trim()
}
else {
$script = $ScriptBlock
}
$PromptTemplateContent = $null
# Validate the prompt template file
if($PromptTemplateFile -ne $null -and $PromptTemplateFile -ne "") {
write-host "PromptTemplateFile: $PromptTemplateFile" -ForegroundColor Cyan
if((Test-Path $PromptTemplateFile)) {
$PromptTemplateContent = (Get-Content $PromptTemplateFile) | Out-String
}
else {
throw "Prompt template file not found: $PromptTemplateFile"
return;
}
}
else {
$PromptTemplateContent = $PromptTemplate
}
# Validate the prompt template
if($PromptTemplateContent -eq $null -or $PromptTemplateContent -eq "") {
if($Verbose) {
Write-Error "[Error] No prompt template provided"
}
else {
throw "No prompt template provided"
}
return;
}
# Validate the prompt settings
if($PromptSettings -eq $null) {
$PromptSettings = @{}
}
# Validate the AI provider
if($AIProvider -eq "OpenAI") {
if($AIProviderKey -eq $null -or $AIProviderKey -eq "") {
# Check for environment variable
$AIProviderKey = $env:OPENAI_API_KEY
if($AIProviderKey -ne $null -and $AIProviderKey.Length -gt 0) {
if($Verbose) {
Write-Host "Using API key from environment variable OPENAI_API_KEY" -ForegroundColor Cyan
}
}
else {
if($Verbose) {
Write-Error "[Error] No API key provided for OpenAI (set the OPENAI_API_KEY environment variable or pass the parameter AIProviderKey)"
}
else {
throw "No API key provided for OpenAI"
}
return;
}
}
}
# Split the script into blocks
if(-not $ShouldSplit) {
$SplitSize = 99999999; # large number
}
$script_blocks = Split-ScriptIntoBlocks -script $script -max_block_length $SplitSize -Verbose:$Verbose
# Write what we have
if($Verbose) {
$total_length = $script_blocks | Measure-Object -Sum Length | Select-Object -ExpandProperty Sum
Write-Host "Blocks: $($script_blocks.Count), Total length: $total_length"
}
$result = @()
# generate the completion
foreach($block in $script_blocks) {
# Generate the prompt
$prompt = $PromptTemplateContent
foreach($setting in $PromptSettings.Keys) {
$prompt = $prompt.Replace("{{" + $setting + "}}", $PromptSettings[$setting])
}
# Replace {{SCRIPT_INPUT}}
$prompt = $prompt.Replace("{{SCRIPT_INPUT}}", (($block -Join "") | Out-String))
$completion = $AIProviderObj.GenerateCompletion($prompt)
$result += @($completion)
}
return ($result -Join "`n").Trim()
}
function Split-ScriptIntoBlocks($script, [int]$max_block_length = 2000, [bool]$Verbose = $true) {
# Split the script into lines and remove carriage returns (if any)
if($script -isnot [array]) { # If the script is a single object, split it into lines
$script = $script -split "`n"
}
# Split into blocks of (up-to) 2000 characters, line-by-line #TODO: Make this configurable. Make this take {} into account. Take block comments into account.
$script_blocks = @()
$current_block = ""
$script | % {
# print a warning if the line is too long
if($_.Length -gt $max_block_length) {
Write-Host "[Warning] Line is longer than $max_block_length characters: $_" -ForegroundColor Yellow
}
# If the block is empty, add the line
if($current_block.Length -eq 0) {
$current_block = $_
return; # Skip to the next line
}
# If the block is too long, add the block to the list and start a new block
if($current_block.Length + $_.Length -gt $max_block_length) {
$script_blocks += $current_block
$current_block = $_
}
# Otherwise, add the line to the block
else {
$current_block += $_
}
}
# Add the last block to the list
if($current_block.Length -gt 0) {
$script_blocks += $current_block
}
# Remove empty blocks
$script_blocks = $script_blocks | ? { $_.Length -gt 0 }
# Print the number of blocks
if($script_blocks.Count -eq 1) {
}
elseif ($script_blocks.Count -eq 0) {
if($Verbose) {
Write-Error "[Error] No script blocks found (This shouldn't happen??)"
}
else {
throw "No script blocks found (This shouldn't happen??)"
}
return;
}
else {
Write-Host "[Warning] Script split into $($script_blocks.Count) blocks" -ForegroundColor Yellow
Write-Host "This is not properly supported yet, so the script will most likely be broken due to missing context." -ForegroundColor Yellow
}
return $script_blocks
}
# Virtual class to override
class AIProvider {
[hashtable] $Settings = @{}
[void] SetSetting([string] $name, [object] $value) {
$this.Settings[$name] = $value
}
[object] GetSetting([string] $name, [object] $default = $null) {
if($this.Settings.ContainsKey($name)) {
return $this.Settings[$name]
}
else {
if($default -eq $null) {
throw "Setting not found: $name"
}
return $default
}
}
[string] GenerateCompletion([string] $prompt) {
throw "NotImplemented"
}
}
# OpenAI class
class OpenAIProvider : AIProvider {
# GenerateCompletion impl
[string] GenerateCompletion([string] $prompt) {
if($prompt -eq 'True') {
throw "Error, `$prompt is 'True', this is not right."
return $null;
}
$API_KEY = $this.GetSetting("APIKey", $env:OPENAI_API_KEY)
if($API_KEY -eq $null -or $API_KEY -eq "") {
throw "No API key provided for OpenAI"
}
$headers = @{
"Authorization" = "Bearer $API_KEY"
"Content-Type" = "application/json"
}
$model = $this.GetSetting("Model", 'text-davinci-003')
$max_tokens = $this.GetSetting("MaxTokens", 1500)
$temperature = $this.GetSetting("Temperature", 0.1)
$stop = $this.GetSetting("StopSeqs", @('---','```'))
$request_body = @{
"model" = $model
"prompt" = $prompt
"temperature" = $temperature
"max_tokens" = $max_tokens
"stop" = $stop
"stream"=$false
"top_p"=1
"n"=1
} | ConvertTo-Json
# Get the response
$endpoint_uri = 'https://api.openai.com/v1/completions'
write-verbose "Sending request to $endpoint_uri"
write-verbose "Request body: $request_body"
write-verbose "Headers: $headers"
$response = Invoke-RestMethod -Uri $endpoint_uri -Method Post -Headers $headers -Body $request_body
# Check for error
if($response.choices -eq $null -or $response.choices.Count -eq 0) {
throw "No response from OpenAI"
}
write-verbose "Response: $($response | convertto-json)"
return (($response.choices | % { $_.text } ) -Join "")
}
}