MacOSдо процесстерди жана ядро ​​кеңейтүүлөрүн кантип коргоо керек

Салам, Хабр! Бүгүн мен MacOSто процесстерди чабуулчулардын чабуулдарынан кантип коргой тургандыгы тууралуу сүйлөшкүм келет. Мисалы, бул антивирус же резервдик система үчүн пайдалуу, айрыкча macOSдо процессти “өлтүрүүнүн” бир нече жолу бар. Бул тууралуу жана кесип астында коргоо ыкмаларын окуңуз.

MacOSдо процесстерди жана ядро ​​кеңейтүүлөрүн кантип коргоо керек

Процессти "өлтүрүүнүн" классикалык жолу

Процессти "өлтүрүүнүн" белгилүү жолу бул процесске SIGKILL сигналын жөнөтүү. Bash аркылуу сиз өлтүрүү үчүн стандарттуу “kill -SIGKILL PID” же “pkill -9 NAME” чакыра аласыз. "Өлтүрүү" буйругу UNIX күндөрүнөн бери белгилүү жана MacOSдо гана эмес, UNIX сыяктуу башка системаларда да жеткиликтүү.

UNIX сыяктуу системалардагыдай эле, macOS процесске экиден башка сигналдарды кармап турууга мүмкүндүк берет - SIGKILL жана SIGSTOP. Бул макалада биринчи кезекте SIGKILL сигналына көңүл бурулат, бул процессти өлтүрүүгө себеп болгон сигнал.

macOS өзгөчөлүктөрү

MacOSдо, XNU ядросундагы өлтүрүү тутумунун чалуусу psignal(SIGKILL,...) функциясын чакырат. Келгиле, колдонуучулардын мейкиндигинде дагы кандай колдонуучунун аракеттерин psignal функциясы деп атаса болорун көрүүгө аракет кылалы. Келгиле, ядронун ички механизмдериндеги psignal функциясына чалууларды жок кылалы (алар анча маанилүү эмес болушу мүмкүн, бирок биз аларды башка макалага калтырабыз 🙂 - кол тамганы текшерүү, эстутум каталары, чыгуу/токтотууну иштетүү, файлды коргоону бузуулар ж. .

Келгиле, карап чыгууну функциядан жана тиешелүү системалык чакыруудан баштайлы жүктөө_менен_аяктоо. Классикалык өлтүрүү чакыруусунан тышкары, MacOS операциялык тутумуна мүнөздүү жана BSDде жок альтернативалуу ыкма бар экенин көрүүгө болот. Эки системалык чалуулардын иштөө принциптери да окшош. Алар psignal ядро ​​функциясына түз чалуулар. Ошондой эле процессти өлтүрүүдөн мурун “кансигнал” текшерүүсү аткарылаарын эске алыңыз - процесс башка процесске сигнал жөнөтө алабы же жокпу; система, мисалы, система процесстерин жок кылууга эч кандай тиркемеге уруксат бербейт.

static int
terminate_with_payload_internal(struct proc *cur_proc, int target_pid, uint32_t reason_namespace,
				uint64_t reason_code, user_addr_t payload, uint32_t payload_size,
				user_addr_t reason_string, uint64_t reason_flags)
{
...
	target_proc = proc_find(target_pid);
...
	if (!cansignal(cur_proc, cur_cred, target_proc, SIGKILL)) {
		proc_rele(target_proc);
		return EPERM;
	}
...
	if (target_pid == cur_proc->p_pid) {
		/*
		 * psignal_thread_with_reason() will pend a SIGKILL on the specified thread or
		 * return if the thread and/or task are already terminating. Either way, the
		 * current thread won't return to userspace.
		 */
		psignal_thread_with_reason(target_proc, current_thread(), SIGKILL, signal_reason);
	} else {
		psignal_with_reason(target_proc, SIGKILL, signal_reason);
	}
...
}

ишке киргизди

Системаны ишке киргизүүдө демондорду түзүүнүн жана алардын иштөө мөөнөтүн көзөмөлдөөнүн стандарттуу жолу ишке киргизилди. Көңүл буруңуз, булактар ​​launchctl'дин macOS 10.10го чейинки эски версиясы үчүн, код мисалдары иллюстративдик максаттар үчүн берилген. Заманбап launchctl ишке киргизилген сигналдарды XPC аркылуу жөнөтөт, launchctl логикасы ага жылдырылды.

Келгиле, тиркемелер так кантип токтотуларын карап көрөлү. SIGTERM сигналын жөнөтүүдөн мурун, колдонмону “proc_terminate” тутумдук чалуу аркылуу токтотууга аракет кылынат.

<launchctl src/core.c>
...
	error = proc_terminate(j->p, &sig);
	if (error) {
		job_log(j, LOG_ERR | LOG_CONSOLE, "Could not terminate job: %d: %s", error, strerror(error));
		job_log(j, LOG_NOTICE | LOG_CONSOLE, "Using fallback option to terminate job...");
		error = kill2(j->p, SIGTERM);
		if (error) {
			job_log(j, LOG_ERR, "Could not signal job: %d: %s", error, strerror(error));
		} 
...
<>

Капоттун астында, proc_terminate, атына карабастан, SIGTERM менен гана эмес, SIGKILL менен сигнал жөнөтө алат.

Кыйыр өлтүрүү - Ресурстун чеги

Кызыктуураак ишти башка системалык чакыруудан көрүүгө болот процесс_саясаты. Бул тутум чалуусунун кеңири колдонулушу колдонмо ресурстарын чектөө болуп саналат, мисалы, индексатор CPU убактысын жана эстутум квотасын чектөө үчүн, система файлдарды кэштөө иш-аракеттери менен олуттуу басаңдабайт. Эгерде колдонмо өзүнүн ресурс чегине жеткен болсо, proc_apply_resource_actions функциясынан көрүнүп тургандай, процесске SIGKILL сигналы жөнөтүлөт.

Бул системалык чалуу процессти өлтүрүшү мүмкүн болсо да, система тутумдук чакырууну чакырган процесстин укуктарын тийиштүү түрдө текшерген эмес. Чынында текшерип жатат болгон, бирок бул шартты айланып өтүү үчүн PROC_POLICY_ACTION_SET альтернативдүү желегин колдонуу жетиштүү.

Демек, эгер сиз тиркеменин CPU колдонуу квотасын "чектөө" болсоңуз (мисалы, 1 нс гана иштөөгө уруксат берүү), анда сиз системадагы каалаган процессти өлтүрө аласыз. Ошентип, кесепеттүү программа системадагы бардык процесстерди, анын ичинде антивирус процессин да жок кыла алат. Ошондой эле кызыктуу нерсе, процессти pid 1 (launchctl) менен өлтүргөндө пайда болгон эффект - SIGKILL сигналын иштетүүгө аракет кылып жатканда ядро ​​​​паника :)

MacOSдо процесстерди жана ядро ​​кеңейтүүлөрүн кантип коргоо керек

көйгөйдү кантип чечүү керек?

Процесстин өлтүрүлүшүнө жол бербөөнүн эң жөнөкөй жолу бул системанын чакыруу таблицасында функция көрсөткүчүн алмаштыруу. Тилекке каршы, бул ыкма көптөгөн себептерден улам маанилүү эмес.

Биринчиден, sysentтин эс тутумунун жайгашкан жерин көзөмөлдөгөн символ XNU өзөк белгиси үчүн гана купуя эмес, бирок ядронун символдорунан табууга болбойт. Функцияны динамикалык түрдө ажыратуу жана андагы көрсөткүчтү издөө сыяктуу эвристикалык издөө ыкмаларын колдонууга туура келет.

Экинчиден, таблицадагы жазуулардын түзүмү ядро ​​түзүлгөн желектерге жараша болот. Эгерде CONFIG_REQUIRES_U32_MUNGING желекчеси жарыяланса, структуранын өлчөмү өзгөрөт - кошумча талаа кошулат sy_arg_munge32. Ядро кайсы желек менен түзүлгөнүн аныктоо үчүн кошумча текшерүү жүргүзүү керек, же болбосо, функция көрсөткүчтөрүн белгилүү болгондорго каршы текшерүү керек.

struct sysent {         /* system call table */
        sy_call_t       *sy_call;       /* implementing function */
#if CONFIG_REQUIRES_U32_MUNGING || (__arm__ && (__BIGGEST_ALIGNMENT__ > 4))
        sy_munge_t      *sy_arg_munge32; /* system call arguments munger for 32-bit process */
#endif
        int32_t         sy_return_type; /* system call return types */
        int16_t         sy_narg;        /* number of args */
        uint16_t        sy_arg_bytes;   /* Total size of arguments in bytes for
                                         * 32-bit system calls
                                         */
};

Бактыга жараша, macOSтун заманбап версияларында Apple процесстер менен иштөө үчүн жаңы API менен камсыз кылат. Endpoint Security API кардарларга башка процесстерге көптөгөн суроо-талаптарга уруксат берүүгө мүмкүндүк берет. Ошентип, сиз жогоруда айтылган API аркылуу процесстерге бардык сигналдарды, анын ичинде SIGKILL сигналын бөгөттөй аласыз.

#include <bsm/libbsm.h>
#include <EndpointSecurity/EndpointSecurity.h>
#include <unistd.h>

int main(int argc, const char * argv[]) {
    es_client_t* cli = nullptr;
    {
        auto res = es_new_client(&cli, ^(es_client_t * client, const es_message_t * message) {
            switch (message->event_type) {
                case ES_EVENT_TYPE_AUTH_SIGNAL:
                {
                    auto& msg = message->event.signal;
                    auto target = msg.target;
                    auto& token = target->audit_token;
                    auto pid = audit_token_to_pid(token);
                    printf("signal '%d' sent to pid '%d'n", msg.sig, pid);
                    es_respond_auth_result(client, message, pid == getpid() ? ES_AUTH_RESULT_DENY : ES_AUTH_RESULT_ALLOW, false);
                }
                    break;
                default:
                    break;
            }
        });
    }

    {
        es_event_type_t evs[] = { ES_EVENT_TYPE_AUTH_SIGNAL };
        es_subscribe(cli, evs, sizeof(evs) / sizeof(*evs));
    }

    printf("%dn", getpid());
    sleep(60); // could be replaced with other waiting primitive

    es_unsubscribe_all(cli);
    es_delete_client(cli);

    return 0;
}

Ошо сыяктуу эле, сигналды коргоо ыкмасын (policy proc_check_signal) камсыз кылган ядродо MAC Саясаты катталышы мүмкүн, бирок API расмий түрдө колдоого алынбайт.

Ядро кеңейтүү коргоо

Системадагы процесстерди коргоодон тышкары, ядро ​​кеңейтүүсүнүн өзүн (kext) коргоо да зарыл. macOS иштеп чыгуучулар үчүн IOKit түзмөк драйверлерин оңой иштеп чыгуу үчүн негизди камсыз кылат. Түзмөктөр менен иштөө үчүн куралдар менен камсыз кылуудан тышкары, IOKit C++ класстарынын инстанцияларын колдонуу менен драйверди стектөө ыкмаларын камсыз кылат. Колдонуучу мейкиндигиндеги тиркеме өзөк-колдонуучу мейкиндигин түзүү үчүн класстын катталган инстанциясын "таба" алат.

Системада класс инстанцияларынын санын аныктоо үчүн ioclasscount утилитасы бар.

my_kext_ioservice = 1
my_kext_iouserclient = 1

Драйвер стекине катталууну каалаган бардык ядро ​​кеңейтүүсү IOServiceден мураска алган классты жарыялоосу керек, мисалы, my_kext_ioservice бул учурда. Колдонуучу колдонмолорун туташтыруу my_kext_iouserclient мисалында IOUserClient'тен мураска алган класстын жаңы инстанциясын түзүүгө алып келет.

Системадан драйверди түшүрүүгө аракет кылып жатканда (kextunload буйругу), "bool terminate(IOOptionBits options)" виртуалдык функциясы чакырылат. Kextunload функциясын өчүрүү үчүн түшүрүүгө аракет кылып жатканда токтотуу үчүн чалууга false кайтаруу жетиштүү.

bool Kext::terminate(IOOptionBits options)
{

  if (!IsUnloadAllowed)
  {
    // Unload is not allowed, returning false
    return false;
  }

  return super::terminate(options);
}

IsUnloadAllowed желекчеси жүктөөдө IOUserClient тарабынан коюлушу мүмкүн. Жүктөө чеги болгондо, kextunload буйругу төмөнкү жыйынтыкты кайтарат:

admin@admins-Mac drivermanager % sudo kextunload ./test.kext
Password:
(kernel) Can't remove kext my.kext.test; services failed to terminate - 0xe00002c7.
Failed to unload my.kext.test - (iokit/common) unsupported function.

Окшош коргоо IOUserClient үчүн жасалышы керек. Класстардын мисалдарын "IOCatalogueTerminate(mach_port_t, uint32_t flag, io_name_t description);" IOKitLib колдонуучу мейкиндиги функциясы аркылуу түшүрсө болот. Колдонуучулардын мейкиндиги тиркемеси "өлмөйүнчө", башкача айтканда, "clientDied" функциясы чакырылмайынча, "токтот" буйругун чакырганда false кайтара аласыз.

Файлды коргоо

Файлдарды коргоо үчүн файлдарга кирүү мүмкүнчүлүгүн чектөөгө мүмкүндүк берген Kauth API колдонуу жетиштүү. Apple иштеп чыгуучуларга масштабдагы ар кандай окуялар жөнүндө эскертмелерди берет; биз үчүн KAUTH_VNODE_DELETE, KAUTH_VNODE_WRITE_DATA жана KAUTH_VNODE_DELETE_CHILD операциялары маанилүү. Файлдарга кирүүнү чектөөнүн эң оңой жолу - бул жол менен - ​​биз файлга жолду алуу жана жол префиксин салыштыруу үчүн "vn_getpath" API колдонобуз. Файл папкаларынын жолдорунун атын өзгөртүүнү оптималдаштыруу үчүн система ар бир файлга кирүүгө уруксат бербестен, аты өзгөртүлгөн папканын өзүнө гана уруксат берерин эске алыңыз. Бул ата-эне жолун салыштыруу жана ал үчүн KAUTH_VNODE_DELETE чектөө зарыл.

MacOSдо процесстерди жана ядро ​​кеңейтүүлөрүн кантип коргоо керек

Бул ыкманын кемчилиги префикстердин саны көбөйгөндүктөн, өндүрүмдүүлүктүн төмөн болушу мүмкүн. Салыштыруу O(prefix*length) менен барабар болбошу үчүн, бул жерде префикс - префикстердин саны, узундук - саптын узундугу, сиз префикстер менен курулган детерминисттик чектүү автоматты (DFA) колдонсоңуз болот.

Берилген префикстердин топтому үчүн DFA түзүү ыкмасын карап көрөлү. Курсорлорду ар бир префикстин башында инициализациялайбыз. Эгерде бардык курсорлор бир символду көрсөтсө, анда ар бир курсорду бир белгиге көбөйтүп, ошол эле сызыктын узундугу бир белгиге чоңураак экенин унутпаңыз. Эгерде символдору эки башка курсор болсо, курсорлорду алар көрсөткөн символ боюнча топторго бөлүп, ар бир топ үчүн алгоритмди кайталаңыз.

Биринчи учурда (курсорлордун астындагы бардык символдор бирдей), биз бир эле сызык боюнча бир гана өткөөлү бар DFA абалын алабыз. Экинчи учурда, функцияны рекурсивдүү чакыруу менен алынган кийинки абалдарга 256 өлчөмүндөгү (символдордун саны жана топтордун максималдуу саны) өтүү таблицасын алабыз.

Келгиле, бир мисал карап көрөлү. Префикстердин жыйындысы үчүн (“/foo/bar/tmp/”, “/var/db/foo/”, “/foo/bar/aba/”, “foo/bar/aac/”) төмөнкүнү ала аласыз DFA. Сүрөт башка мамлекеттерге алып баруучу өткөөлдөрдү гана көрсөтөт, башка өтүүлөр акыркы болбойт.

MacOSдо процесстерди жана ядро ​​кеңейтүүлөрүн кантип коргоо керек

DKA мамлекеттеринен өтүп жатканда 3 учур болушу мүмкүн.

  1. Акыркы абалга жетти - жол корголгон, биз KAUTH_VNODE_DELETE, KAUTH_VNODE_WRITE_DATA жана KAUTH_VNODE_DELETE_CHILD операцияларын чектейбиз
  2. Акыркы абалга жеткен жок, бирок жол "бүттү" (нөлдүк терминаторго жетти) - жол ата-эне, KAUTH_VNODE_DELETEди чектөө керек. Эгерде vnode папка болсо, аягында '/' кошушуңуз керек экенин эске алыңыз, антпесе ал аны “/foor/bar/t” файлына чектеши мүмкүн, бул туура эмес.
  3. Акыркы абалга жеткен жок, жол бүтпөйт. Бул префикстердин бири да дал келбейт, биз чектөө киргизбейбиз.

жыйынтыктоо

Иштеп жаткан коопсуздук чечимдеринин максаты - колдонуучунун жана анын маалыматтарынын коопсуздук деңгээлин жогорулатуу. Бир жагынан алганда, бул максатка операциялык тутумдун өзү "алсыз" болгон кемчиликтерди жапкан Acronis программалык продуктусун иштеп чыгуу аркылуу жетишилет. Башка жагынан алганда, биз ОС жагында жакшыртылышы мүмкүн болгон коопсуздук аспектилерин бекемдөөгө көңүл бурбашыбыз керек, айрыкча, мындай аялуу жерлерди жабуу биздин продукт катары туруктуулукту жогорулатат. Алсыздык Apple продуктунун коопсуздук тобуна билдирилген жана macOS 10.14.5 (https://support.apple.com/en-gb/HT210119) менен оңдолгон.

MacOSдо процесстерди жана ядро ​​кеңейтүүлөрүн кантип коргоо керек

Мунун баары сиздин утилитаңыз расмий түрдө ядродо орнотулган болсо гана жасалышы мүмкүн. Башкача айтканда, тышкы жана керексиз программалык камсыздоо үчүн мындай жылчыктар жок. Бирок, сиз көрүп тургандай, антивирус жана резервдик системалар сыяктуу мыйзамдуу программаларды коргоо да эмгекти талап кылат. Бирок азыр MacOS үчүн жаңы Acronis өнүмдөрү системадан түшүрүүдөн кошумча коргоого ээ болот.

Source: www.habr.com

Комментарий кошуу