可以通过创建DisableInflow
电源管理声明来对Mac笔记本电脑的电源适配器执行软件控制的断开连接。
代码可用于创建所述断言。以下是一个有效的示例,它将创建此断言,直到进程被终止:
#include <IOKit/pwr_mgt/IOPMLib.h>
#include <unistd.h>
int main()
{
IOPMAssertionID neverSleep = 0;
IOPMAssertionCreateWithName(kIOPMAssertionTypeDisableInflow,
kIOPMAssertionLevelOn,
CFSTR("disable inflow"),
&neverSleep);
while (1)
{
sleep(1);
}
}
此操作成功运行,并且在进程运行时通过软件断开了电源适配器的连接。
但是,有趣的是,我能够以普通用户的身份运行此代码,而没有root特权,这本来是不会发生的。例如,请注意Apple开源存储库中this file中的评论:
// Disables AC Power Inflow (requires root to initiate)
#define kIOPMAssertionTypeDisableInflow CFSTR("DisableInflow")
#define kIOPMInflowDisableAssertion kIOPMAssertionTypeDisableInflow
我发现一些代码显然可以执行与充电器的实际通信;可以找到here。来自this file的以下功能似乎特别受关注:
IOReturn
AppleSmartBatteryManagerUserClient::externalMethod(
uint32_t selector,
IOExternalMethodArguments * arguments,
IOExternalMethodDispatch * dispatch __unused,
OSObject * target __unused,
void * reference __unused )
{
if (selector >= kNumBattMethods) {
// Invalid selector
return kIOReturnBadArgument;
}
switch (selector)
{
case kSBInflowDisable:
// 1 scalar in, 1 scalar out
return this->secureInflowDisable((int)arguments->scalarInput[0],
(int *)&arguments->scalarOutput[0]);
break;
// ...
}
// ...
}
IOReturn AppleSmartBatteryManagerUserClient::secureInflowDisable(
int level,
int *return_code)
{
int admin_priv = 0;
IOReturn ret = kIOReturnNotPrivileged;
if( !(level == 0 || level == 1))
{
*return_code = kIOReturnBadArgument;
return kIOReturnSuccess;
}
ret = clientHasPrivilege(fOwningTask, kIOClientPrivilegeAdministrator);
admin_priv = (kIOReturnSuccess == ret);
if(admin_priv && fOwner) {
*return_code = fOwner->disableInflow( level );
return kIOReturnSuccess;
} else {
*return_code = kIOReturnNotPrivileged;
return kIOReturnSuccess;
}
}
请注意在运行代码之前,如何在secureInflowDisable()
中检查root特权。还要注意在同一文件中的该初始化代码,再次需要root特权,如注释中明确指出的那样:
bool AppleSmartBatteryManagerUserClient::initWithTask(task_t owningTask,
void *security_id, UInt32 type, OSDictionary * properties)
{
uint32_t _pid;
/* 1. Only root processes may open a SmartBatteryManagerUserClient.
* 2. Attempts to create exclusive UserClients will fail if an
* exclusive user client is attached.
* 3. Non-exclusive clients will not be able to perform transactions
* while an exclusive client is attached.
* 3a. Only battery firmware updaters should bother being exclusive.
*/
if ( kIOReturnSuccess !=
clientHasPrivilege(owningTask, kIOClientPrivilegeAdministrator))
{
return false;
}
// ...
}
从same SO question above的代码(问题本身,而不是答案)开始,对于sendSmartBatteryCommand()
函数,我编写了一些代码,该代码调用将kSBInflowDisable
作为选择器的函数(代码中的变量which
)。
与使用断言的代码不同,此代码仅作为root用户工作。如果以普通用户身份运行,IOServiceOpen()
会奇怪地返回kIOReturnBadArgument
(而不是我所期望的kIOReturnNotPrivileged
)。也许这与上面的initWithTask()
方法有关。
我需要使用与此智能电池管理器kext不同的选择器进行呼叫。即使这样,由于IOConnectCallMethod()
失败,我什至无法进入IOServiceOpen()
,大概是因为initWithTask()
方法阻止了任何非root用户打开服务。
因此,问题是: IOPMAssertionCreateWithName()
如何能够在没有root特权的情况下创建DisableInflow
断言?
我唯一想到的是是否存在一个根拥有的进程,该进程将请求转发到该进程,并且该进程执行将IOServiceOpen()
和后来的IOConnectCallMethod()
称为root的实际工作。
但是,我希望有另一种方法来调用Smart Battery Manager kext,该方法不需要root用户(不涉及IOServiceOpen()
调用。)使用IOPMAssertionCreateWithName()
本身是在我的应用程序中是不可能的,因为我需要在该kext中调用其他选择器,而不是禁用流入的选择器。
这实际上也可能是一个安全漏洞,一旦收到有关此问题的警报,Apple现在将在将来的版本中修复此漏洞。那太糟糕了,但是可以理解。
尽管在macOS中有可能以root身份运行,但除非绝对必要,否则显然希望避免特权提升。另外,将来我想在iOS下运行相同的代码,据我了解,不可能以root身份运行任何东西(请注意,这是我为个人使用而开发的应用;我了解链接到IOKit消除了在应用商店中发布该应用的任何机会。