它向我指出(在 powerShell,复制bash Parallel ping ),我可以从Internet上加载一个函数,如下所示
iex (irm https://raw.githubusercontent.com/proxb/AsyncFunctions/master/Test-ConnectionAsync.ps1)
: URL引用 test-connectionAsync.ps1
包含两个函数: ping-subnet
和 test-connectionAseNAsync
这让我想知道我是否可以定义旁路功能在我的个人模块中,它是虚拟函数,一旦被调用,它们就会永久覆盖。例如,
function Ping-Subnet <mimic the switches of the function to be loaded> {
if <function is not already loaded from internet> {
iex (irm https://raw.githubusercontent.com/proxb/AsyncFunctions/master/Test-ConnectionAsync.ps1)
}
# Now, somehow, permanently overwrite Ping-Subnet to be the function that loaded from the URL
Ping-Subnet <pass the switches that we mimicked to the required function that we have just loaded>
}
这将非常简单地允许我直接从我的模块引用许多有用的脚本,但是无需在加载模块时必须从Internet上加载它们(即仅按需加载功能,我调用它们,除非我需要它们,否则我通常永远不会调用这些功能)。
It was pointed out to me (in PowerShell, replicate bash parallel ping) that I can load a function from the internet as follows:
iex (irm https://raw.githubusercontent.com/proxb/AsyncFunctions/master/Test-ConnectionAsync.ps1)
The url referenced Test-ConnectionAsync.ps1
contains two functions: Ping-Subnet
and Test-ConnectionAsync
This made me wonder if I could then define bypass functions in my personal module that are dummy functions that will be permanently overridden as soon as they are invoked. e.g.
function Ping-Subnet <mimic the switches of the function to be loaded> {
if <function is not already loaded from internet> {
iex (irm https://raw.githubusercontent.com/proxb/AsyncFunctions/master/Test-ConnectionAsync.ps1)
}
# Now, somehow, permanently overwrite Ping-Subnet to be the function that loaded from the URL
Ping-Subnet <pass the switches that we mimicked to the required function that we have just loaded>
}
This would very simply allow me to reference a number of useful scripts directly from my module but without having to load them all from the internet upon loading the Module (i.e. the functions are only loaded on demand, when I invoke them, and I will often never invoke the functions unless I need them).
发布评论
评论(4)
您可以使用解析器在远程脚本中找到功能并将其加载到您的范围中。这不会是自我升级功能,但应该比您想完成的工作更安全。
You could use the Parser to find the functions in the remote script and load them into your scope. This will not be a self-updating function, but should be safer than what you're trying to accomplish.
结果:
Result:
是的,它应该起作用。调用
test-connectionAsync.ps1
在一个函数中,将在包装函数的范围中创建定义的函数。您将能够调用任何包装功能,直到功能的范围结束为止。如果您将包装器命名和包装功能不同,则可以检查是否已使用类似...
否则,您需要获得更多的创意。
也就是说, 继续谨慎 。像这样,远程代码执行充满了安全问题,尤其是在我们谈论的方式中,没有验证
test-connectionAsenc.ps1
。Yes, it should work. Calling
Test-ConnectionAsync.ps1
from with-in a function will create the functions defined with-in, in the wrapping function's scope. You will be able to call any wrapped functions until the function's scope ends.If you name the wrapper and wrapped functions differently, you can check whether the function has been declared with something like...
Otherwise, you need to get more creative.
This said, PROCEED WITH CAUTION. Remote code execution, like this, is fraught with security issues, especially in the way we're talking about it i.e., no validation of
Test-ConnectionAsync.ps1
.fors1k的答案值得获得信用,因为提出了该方法的巧妙基础< /strong>:
在用
Dynamic Module 中下载并执行远程脚本的内容。脚本的功能将被自动出口并获得 session-globally [1]
请注意,动态模块并不容易发现,因为它们没有在
get-module
的输出;但是,您可以通过.source
属性。 powershell/module/microsoft.powershell.core/get-command“ rel =“ nofollow noreferrer”>get-command
:如果您尝试在不应影响会话的全局状态的脚本中使用该技术,则下载的函数可用 session -globally 可能是不希望的 - 请参阅底部解决方案的部分。
然后重新启用该函数,假设原来的存根函数已被同名下载版本替换,并将收到的参数传递。
虽然Fors1k的解决方案通常会起作用,但这里是一种简化的,可靠的替代方案,可以防止潜在的代码重新执行:
具体来说,此实现确保了:
em> Aliases 在远程脚本中定义的
是导出的。此外(只需删除
+“`nexport -modulemember -function * -Alias *'
从上面的代码中删除。重新转换鲁棒性针对 new ,模块定义的函数实现 - 即使存根函数在子范围内运行,例如在(非点源)中脚本。
$ myInvocation.line | iex
(iex
是invoke-expression
cmdlet)会导致无限循环,因为当时的存根功能本身仍然有效。
所有收到的参数均在不重新评估的情况下通过重新发电。
使用内置的魔术来剥落自动
$ args
variable(@args
)仅通过接收到的已经扩展的参数,支持两者都命名和位置参数。 [2]$ myInvocation.line | iex
有两个潜在的问题:如果调用命令行包含多个命令,则它们是 ash em 重复。
(get-pscallstack)[1] .position.text
$ myInvocation.line
来解决此特定问题下一个问题。两种
$ myInvocation.line
和(get-pscallstack)[1] .position.text
包含在 em> doducted 中传递的参数/em>(未评估)表格,这会导致其通过Invoke-Expression
进行重新评估,并且其危险至少在假设上,此重新评估可能涉及冗长的命令,其输出用作其输出为参数或更糟糕的是,具有副作用的命令无法重复或不应该重复。将技术范围划分到给定的本地脚本:
如果您尝试在脚本中使用该技术,则下载的功能可用 session-globally 可能是不必要的。 不应该影响会议的全球状态;也就是说,您可能希望在脚本退出时通过动态模块导出的功能消失。
这需要有两个额外的步骤:
将动态模块管道到 import-module ,这是能够在使用
Remove-Module
呼叫
remove-module
带有动态模块,然后退出以卸载。[1]这假定
new-Module
调用本身是在模块之外进行的;如果在模块中制成,至少该模块的命令会看到自动出口的函数;如果该模块使用隐式导出行为(这是罕见且不建议),则该模块的自动出口功能将包含在该模块的导出中,因此再次成为session-cession-cession-cession-clobally。[2]这个魔术有一个限制,但是,它只会表面很少:
[switch]
parameters 直接连接的布尔值参数不是支持的(例如,-casessentive:$ true
) - 参见此答案。Fors1k's answer deserves the credit for coming up with the clever fundamentals of the approach:
Download and execute the remote script's content in a dynamic module created with
New-Module
(whose built-in alias isnmo
), which causes the script's functions to be auto-exported and to become available session-globally[1]Note that dynamic modules aren't easy to discover, because they're not shown in
Get-Module
's output; however, you can discover them indirectly, via the.Source
property of the command-info objects output byGet-Command
:That the downloaded functions become available session-globally may be undesired if you're trying to use the technique inside a script that shouldn't affect the session's global state - see the bottom section for a solution.
Then re-invoke the function, under the assumption that the original stub function has been replaced with the downloaded version of the same name, passing the received arguments through.
While Fors1k's solution will typically work, here is a streamlined, robust alternative that prevents potential, inadvertent re-execution of code:
Specifically, this implementation ensures:
That aliases defined in the remote script are exported as well (just remove
+ "`nExport-ModuleMember -Function * -Alias *"
from the code above if that is undesired.That the re-invocation robustly targets the new, module-defined implementation of the function - even if the stub function runs in a child scope, such as in a (non-dot-sourced) script.
$MyInvocation.Line|IEX
(iex
is a built-in alias of theInvoke-Expression
cmdlet) would result in an infinite loop, because the stub function itself is still in effect at that time.That all received arguments are passed through on re-invocation without re-evaluation.
Using the built-in magic of splatting the automatic
$args
variable (@args
) passes only the received, already expanded arguments through, supporting both named and positional arguments.[2]$MyInvocation.Line|IEX
has two potential problems:If the invoking command line contained multiple commands, they are all repeated.
(Get-PSCallStack)[1].Position.Text
for$MyInvocation.Line
, but that still wouldn't address the next problem.Both
$MyInvocation.Line
and(Get-PSCallStack)[1].Position.Text
contain the arguments that were passed in unexpanded (unevaluated) form, which causes their re-evaluation byInvoke-Expression
, and the perils of that are that, at least hypothetically, this re-evaluation could involve lengthy commands whose output served as arguments or, worse, commands that had side effects that cannot or should not be repeated.Scoping the technique to a given local script:
That the downloaded functions become available session-globally may be undesired if you're trying to use the technique inside a script that shouldn't affect the session's global state; that is, you may want the functions exported via the dynamic module to disappear when the script exits.
This requires two extra steps:
Piping the dynamic module to
Import-Module
, which is the prerequisite for being able to unload it before exiting withRemove-Module
Calling
Remove-Module
with the dynamic module before exiting in order to unload it.[1] This assumes that the
New-Module
call itself is made outside of a module; if it is made inside a module, at least that module's commands see the auto-exported functions; if that module uses implicit exporting behavior (which is rare and not advisable), the auto-exported functions from the dynamic module would be included in that module's exports and therefore again become available session-globally.[2] This magic has one limitation, which, however, will only rarely surface:
[switch]
parameters with a directly attached Boolean argument aren't supported (e.g.,-CaseSensitive:$true
) - see this answer.