Kimapr 10 Опубликовано: 24 января, 2019 (изменено) Бывали ли у вас когда-нибудь такие ситуации, когда необходимо защитить какую-либо таблицу от записи? Например, вы пишите операционную систему в OpenComputers, и хотите хоть какую-то защиту от вирусов. Бедные таблицы всегда в очень большой опасности. С tprotect ваши таблицы будут в безопасности, так как другие программы вместо оригинала таблицы получат её зеркальную копию. А когда вы захотите снова в нее что-то записать, вы сможете это сделать (оригинал же у вас?). Вообщем вот ее код : local tprotect={} local raw_rawset=rawset -- Сохраняем rawset для дальнейшего пользования local raw_rawget=rawget -- Сохраняем rawget для дальнейшего пользования local getmetatable=getmetatable -- local setmetatable=setmetatable -- local type=type -- Дополнительная зашыта local error=error -- local assert=assert -- local protectid={} local nextid={} function rawget(t,k) if type(t)=="table" and raw_rawget(t,protectid) then error("СЕРЬЁЗНАЯ ПРОБЛЕМА БЕЗОПАСНОСТИ ДЕТЕКТЕД. УНИЧТОЖАЕМ ОПАСНОСТЬ...",2) end return raw_rawget(t,k) end local raw_next=next -- НИКТО НЕ ДОЛЖЕН УЗНАТЬ МАСТЕР-КЛЮЧ!!! function next(t,k) if type(t)=="table" and raw_rawget(t,protectid) then error("СЕРЬЁЗНАЯ ПРОБЛЕМА БЕЗОПАСНОСТИ ДЕТЕКТЕД. УНИЧТОЖАЕМ ОПАСНОСТЬ...",2) end local ok,k,v=xpcall(raw_next,debug.traceback,t,k) if not ok then error(k,0) end return k,v end local raw_ipairs=ipairs function ipairs(...) local f,t,z=raw_ipairs(...) return function(t,k) if type(t)=="table" and raw_rawget(t,protectid) then error("СЕРЬЁЗНАЯ ПРОБЛЕМА БЕЗОПАСНОСТИ ДЕТЕКТЕД. УНИЧТОЖАЕМ ОПАСНОСТЬ...",2) end return f(t,k) end,t,z end function rawset(t,k,v) -- Потому что в защитные копии таблиц можно было бы записывать. Хоть это бы и не отразилось бы на оригинале, но при попытке индекснуть поле защитной копии будет подложено подмененное поле в обход __index if k==protectid then error("СЕРЬЁЗНАЯ ПРОБЛЕМА БЕЗОПАСНОСТИ ДЕТЕКТЕД. УНИЧТОЖАЕМ ОПАСНОСТЬ...",2) end assert(type(t)=="table","bad argument #1 to rawset (table expected, got "..type(t)..")") assert(type(k)~="nil","bad argument #2 to rawset (table index is nil)") local mt=getmetatable(t) local no_set=raw_rawget(t,protectid) or (type(mt)=="table" and raw_rawget(mt,protectid)) if no_set then error("таблица рид-онли! Аксес дэняйд!",2) end raw_rawset(t,k,v) return t end function tprotect.protect(t) local tcopy={[protectid]=true} local mto=getmetatable(t) local tcopy_mt=type(mto)=="table" and mto or {} local mt={[protectid]=true} function mt:__index(k) local x=t[k] if tcopy_mt.__index and not x then return tcopy_mt.__index(t,k) end return t[k] end function mt:__pairs(self) if tcopy_mt.__pairs then return tcopy_mt.__pairs(t) end local function iter(x,i) assert(x==self) return next(t,i) end return iter,self,nil end function mt:__ipairs(self) if tcopy_mt.__ipairs then return tcopy_mt.__ipairs(t) end local f,x,i=ipairs(self) local function iter(self,i) return f(t,i) end return iter,x,i end function mt:__newindex(k,v) if tcopy_mt.__newindex then -- Мы доверяем нашим клиентам! return tcopy_mt.__newindex(self,k,v) end error("СРЕДНЕНЬКАЯ ПРОБЛЕМА БЕЗОПАСНОСТИ ДЕТЕКТЕД. УНИЧТОЖАЕМ ОПАСНОСТЬ...",2) end mt.__metatable={"Хочешь проблем? Попытайся взломать tprotect!"} setmetatable(mt,{__index=function(self,i) local v=tcopy_mt if type(v)=="function" then return function(self,...) return v(t,...) end end return v end}) setmetatable(tcopy,mt) return tcopy,tcopy_mt end local tprotect_t,tprotect_mt=tprotect.protect(tprotect) -- Защитим нашу библиотечку return tprotect_t А теперь документация, а то я пишу код непонятно: function tprotect.protect(table:t):table -- Возвращает "зеркало" таблицы t и её метатаблицу (у "зеркальной" таблицы она неизменяема). "Зеркальная" таблица защищена от записи и всегда является отражением t. Если вы хотите защитить таблицу t, то на публичное (доступное для других программ, от которых вам хотелось бы защитить t) место, где вы ставите обычно таблицу t, ставьте "зеркальную" таблицу Изменено 26 января, 2019 пользователем Kimapr bugzz detectedd 6 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
kcalBxoF 15 Опубликовано: 24 января, 2019 Позволю себе немножко критики, как человеку который любит играться в сфере ИБ. math.random - это псевдорандомное число, оно уязвимо. Цитата local function newKey() return {math.random(10000,99999)} end Такого рода ключ можно сбрутфорсить, причем сделается это довольно быстро. Делать что-то для безопасности в скриптовой ОСи чуть менее чем бессмысленно, как по мне - весь код, даже если его обфусцировать, читается на раз два. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Totoro 3 563 Опубликовано: 25 января, 2019 Не отрицая сути (ключ от таблицы нужно как минимум где-то хранить, что создает новую точку уязвимости), тем не менее был бы очень рад увидеть практическую имплементацию брутфорса пароля. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
ECS 1 904 Опубликовано: 25 января, 2019 9 часов назад, kcalBxoF сказал: Такого рода ключ можно сбрутфорсить, причем сделается это довольно быстро. Здесь ключ - это не число, а таблица с уникальным адресом. По факту для подобного даже рандом не нужен, можно было вообще возвращать пустую таблицу или функцию, служащую идентификатором для разблокировки. А поскольку debug в опенкомпах крайне лимитирован, и доступа к локальным переменным нет, то и выпотрошить весь стек оперативы в поисках ключа для брутфорса не получится. 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
kcalBxoF 15 Опубликовано: 25 января, 2019 7 часов назад, ECS сказал: Здесь ключ - это не число, а таблица с уникальным адресом. А этот момент я упустил, т.к в код не особо вчитывался, думал что используется именно число из этого рандома. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
astral17 65 Опубликовано: 25 января, 2019 24.01.2019 в 16:22, Kimapr сказал: if getmetatable(t).isOpened(protectid) Интересная защита), сломать функцию rawset , чтобы она не работала) т.е. она получает метатаблицу и запускает функцию в ней, во-первых если метатаблицы нету, краш), если метатаблица есть, то всё равно краш, т.к. в ней нету этой функции) Затем tprotect.open не работает, причина проста, mto может равняться nil , т.е. если создать чистую таблицу, и её защитить, получим epic fail... возвращаемая таблица через getmetatable(ptbl) , где было вызвано tprotect.protect(ptbl) является mt2 и её можно редактировать, такая же проблема вообще и на саму ptbl распространяется, т.к. __newindex работает только в случае, если такого ключа нету, если ключ есть, то нужно использовать __index , однако получив таблицу mt2 даже с доступом на чтение мы сможем спокойно выудить все значения, и protectid и списочек nonpi (я так и не понял, зачем он вообще нужен, как вариант посолить, чтобы сложнее было вытянуть protectid, но в таком случае слишком мало соли). Вообще protectid можно получить просто через tprotect.isProtected , т.к. там получается метатаблица у таблицы, которую мы передаём внутрь и обращается по индексу protectid , т.е. секретным назвать его трудно. Но вернёмся к нашим баранам, mt2 допустим у неё есть __index тоже, значит we need to go deeper , смотрим на метатаблицу mt2, она не защищена, т.е. её можно спокойно изменять и мы видим спокойно все её значения Скрытый текст снимаем защиту на изменение mt2 -> profit 24.01.2019 в 16:22, Kimapr сказал: if getmetatable(t).isOpened(protectid) or ((not tprotect.isProtected(t)) and not cantrawset) then возвращаемся сюда, порядок в lua слева на право, т.е. в начале оно споткнётся об nil , а потом проверит, а защищалась ли таблица вообще?, т.е. нужно поменять местами для начала if ((not tprotect.isProtected(t)) and not cantrawset) or getmetatable(t).isOpened(protectid) then если не защищено, то оно не будет пытать метаметод isOpened , однако mt2 мы можем спокойно редактировать, т.е. isProtected и cantrawset мы можем управлять их значениями. Ещё можно атакуемую таблицу заменить на свою таблицу, дождаться пока её не попробуют открыть, и таким образом заполучить ключ (MitM attack) Либо пойти более общим путём, раз getmetatable не сохраняется, то просто подменить его и вытянуть интересующие таблички в момент защиты, открытия и т.д. и т.п., либо в setmetatable сделать так, чтобы устанавливались во время защиты свои, т.е. их тоже нужно копировать и защищать) Затем функция open немного бесполезна, т.к. получается, что таблица на момент выполнения кода не защищена, лучше сделать вариацию, где возвращается таблица, изменяя которую изменяется оригинальная, причём защищённая таблица остаётся защищённой. Также сам tprotect тоже нужно защищать, т.к. перезаписать функции библиотеки хватит, чтобы сломать её защиту (функции хранятся в открытом виде в package, где можно спокойно подменить, и require будет возвращать её) , также require тоже нужно защищать. Т.е. по факту защита работает успешно только для защищённых ранее и не открывая доступ во время выполнения чего-то, что может иметь плохие желания. Ладно, пока что на этом хватит, резюмируя всё выше сказанное, её ещё пилить и пилить. А вообще возникает чувство, что автор её даже не проверял, т.к. работают только 2 функции protect и unprotect , а функция rawset вообще под чистую сломана. Ну надеюсь в следующей версии автор учтёт хотя бы часть написанного тута, и следующая версия библиотеки будет лучше. 3 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Kimapr Автор темы 10 Опубликовано: 25 января, 2019 24.01.2019 в 18:22, Kimapr сказал: Бывали ли у вас когда-нибудь такие ситуации, когда необходимо защитить какую-либо таблицу от записи? Например, вы пишите операционную систему в OpenComputers, и хотите хоть какую-то защиту от вирусов. Бедные таблицы всегда в очень большой опасности. Как овечки. tprotect - это пастух, который защитит ваши таблицы от записи, а когда вы захотите снова в нее что-то записать, вы сможете это сделать (если не потеряете ключ). Вообщем вот ее код (он небольшой): Показать содержимое local tprotect={} local raw_rawset=rawset -- Обязательно! Без этой строки вся библиотека безполезна! local protectid=tostring(math.random(10000,99999)) local nonpi={ tostring(math.random(10000,99999)), tostring(math.random(10000,99999)), tostring(math.random(10000,99999)), tostring(math.random(10000,99999)) } local function newKey() return {math.random(10000,99999)} end function _G.rawset(t,k,v) -- Ну и без этой функции тоже local cantrawset=getmetatable(t)[protectid.."cantrawset"] or t[protectid.."cantrawset"] local cantset=getmetatable(t)[protectid.."cantset"] or t[protectid.."cantset"] if getmetatable(t).isOpened(protectid) or ((not tprotect.isProtected(t)) and not cantrawset) then raw_rawset(t,k,v) elseif not cantset then t[k]=v end end function tprotect.protect(t) -- Защитить таблицу от записи и вернуть ключ разблокировки assert(type(t)=="table","bad argument #1 to protect (table expected, got "..type(t)..")") local key=newKey() local opened=false local mto=getmetatable(t) local mt local mt2=setmetatable( { [protectid]=true, ___open=function(k) if k==key then opened=true return true end return false end, ___close=function(k) if k==key then opened=false return true end end, ___isOpened=function(pi) if pi==protectid then return opened else return nil end end, [nonpi[1]]=true, [nonpi[2]]=true, [nonpi[3]]=true, [nonpi[4]]=true, ___unprotect=function(k) if k==key then return mt else return false end end }, {__pairs=function(self)return function()end,self,nil end, __ipairs=function(self)return function()end,self,0 end, __newindex=function(self,k,v)end, [protectid.."cantrawset"]=true, [nonpi[1].."cantrawset"]=true, [nonpi[2].."cantrawset"]=true, [nonpi[3].."cantrawset"]=true, [nonpi[4].."cantrawset"]=true, [protectid.."cantset"]=true, [nonpi[1].."cantset"]=true, [nonpi[2].."cantset"]=true, [nonpi[3].."cantset"]=true, [nonpi[4].."cantset"]=true,}) mt=setmetatable({ __newindex=function(self,k,v) if not opened then error("table \""..tostring(self).."\" is protected, you can't write to it!") end if mto.__newindex then return mto.__newindex(self,k,v) end return raw_rawset(self,k,v) end, __metatable=mt2 },{__index=mto,__newindex=function(self,k,v) if not opened then error("table \""..tostring.."\" is protected, you can't write to it!") end return raw_rawset(self,k,v) end}) setmetatable(t,mt) return key end function tprotect.open(t,k) -- "Открыть" таблицу: в неё можно будет писать. Для этого нужен ключ разблокировки, который вернул tprotect.protect return getmetatable(t).___open(k) end function tprotect.close(t,k) -- "Закрыть" таблицу: она снова будет защищена от записи. Опять же, нужен ключ. return getmetatable(t).___close(k) end function tprotect.unprotect(t,k) -- Разблокировка таблицы, отмена tprotect.protect. Нужен ключ, а то библиотека безполезна local mt,mto=getmetatable(t).___unprotect(k) if mt then mt.__metatable=nil setmetatable(t,mto) return true else return false end end function tprotect.isProtected(t) -- Защищена ли таблица? return getmetatable(t)[protectid] end return tprotect А теперь документация, а то я пишу код непонятно: function tprotect.protect(table:t):table -- Установить защиту на таблицу. После этого в нее нельзя будет записывать, не имея ключ, который эта функция и возвращает. Если у таблицы t была метатаблица, то она останется и будет работать дальше, но сменить её будет нельзя до вызова tprotect.unprotect function tprotect.open(table:t,table:key):boolean -- "Открыть" таблицу ключем. Если таблица key - это тот самый ключ, который вернула tprotect.protect, то вернёт возможность записи в таблицу. function tprotect.close(table:t,table:key):boolean -- "Закрыть" таблицу тем же ключем. Обязательно вызывайте эту функцию, после того, как закончите запись или ваша таблица будет снова в опасности. function tprotect.unprotect(table:t,table:key):boolean -- Отменяет все, что наделала функция tprotect.protect. Вызывайте её, когда вам надоест защита вашей таблицы или когда вы захотите сменить метатаблицу. Новая версия идет! Переписано с нуля, теперь больше безопасности и меньше нерабочести! Ну и сложности тоже поменьше. А то как то сложновато. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
astral17 65 Опубликовано: 25 января, 2019 (изменено) Дубль 2, короче сдаюсь в этот раз получилось гораздо качественнее, уязвимости есть, но не очень критические и легко фиксятся. Так сказать защита rawset не очень хороша), а если точнее, то error не обязательно выходит из функции, например если сделать так, как на скрине, то сразу понятно, что код продолжит работу. Т.е. нужно либо хранить персональный error , либо дописать везде return nil или что-то похожее), похожая проблема с getmetatable и setmetatable , т.е. если их переписать, то получится, что мы сможем выхукнуть таблицы в момент их защиты. Также это дело касается type , т.к. я могу обломать валидацию mto на таблицу и запихнуть через __metatable функцию, т.е. tcopy_mt = mto = myfunction , а если __index это функция, то при обращении к метатаблице я смогу получить и саму чистую метатаблицу, т.е. mt, а сама она никак не защищена, как сиё сделать, (цель вообще вытянуть protectid) , смотрим, нам нужно обратиться в mt к значению, которого нету например _add , а значит вызывается __index в который помещается mt, "__add" и тут мы и хукаем mt. (как вытянуть не проверялось, и писалось почти с закрытыми глазами, так что скорее всего сиё чудо только в теории, завтра чекну) Изменено 25 января, 2019 пользователем astral17 2 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Kimapr Автор темы 10 Опубликовано: 26 января, 2019 (изменено) Проблематичненько! Я даже не заметил, что можно их переопределить, как это сделал я. Шас исправим-с. Вот патч: local getmetatable=getmetatable -- local setmetatable=setmetatable -- local type=type -- Дополнительная зашыта local error=error -- local assert=assert -- Библиотека все еще уязвима к подмене этих функций до её загрузки. Так что её нужно загружать до загрузки чего-либо ещё опасного Изменено 26 января, 2019 пользователем Kimapr Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
astral17 65 Опубликовано: 26 января, 2019 (изменено) Ну, из уязвимостей вроде бы и всё, конечно же проблема до сих пор возникает "как получить эту библиотеку, если зловред уже работает", т.е. он может спокойно подменить require , package и т.д. , т.е. даже если библиотека загружена, ты не можешь удостовериться в том, что это правда она, однако то, что сама таблица защищена уже гарантирует, что те, кто получил её не обнаружат сюрприз. Как фиксить такое фз, и это уже просто чисто для размышлений (но было бы неплохо, если можно было подтвердить). Было бы неплохо, если сам код был под спойлером в теге "код". Как знаешь, в таблицах, которые хочешь защитить могут находиться также и другие таблицы. Иногда хочется защитить и их (но не всегда), поэтому хотелось бы видеть функцию, которая будет рекурсивно защищать все таблицы от изменений, которые есть в защищаемой таблице. (не забудь про возможность зацикливания, например в защищаемой таблице может вдруг оказаться ссылка на её саму). Ну и на последок, то, что в getmetatable от зеркала возвращается строка защитная, не очень хорошо, иногда хотелось бы видеть метатаблицу, оригинальной (конечно же readonly), тут тогда возникает вопрос, до какой глубины идти?, ответ: до тех пор, пока есть метатаблица у метатаблицы и она ещё не защищена (в кратце расширение к рекурсивной защите). В принципе это будет создавать дополнительные нагрузки на память, поэтому можно поставить флажок, нужно ли так делать. Вроде бы большего ожидать от этой библиотеки смысла скорее всего нету (до тех пор, пока она касается только защиты таблиц). А за старания респект. UPD: короче, нужен багфикс сего чуда Если вкратце, то при обращении к метаметодам зеркала получается, что оно берёт сырые значения из метатаблицы (вот как раз таки причина, почему у меня не удалась та атака с __add , ну да ладно, она хотя бы показала, что в теории любая глобальная переменная может быть злом), а это может оказаться довольно довольно важной вещью таблицы (т.е. нужно сделать будет редирект всех метаметодов, либо изменить структуру работы). Изменено 26 января, 2019 пользователем astral17 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Kimapr Автор темы 10 Опубликовано: 26 января, 2019 (изменено) 3 часа назад, astral17 сказал: Ну, из уязвимостей вроде бы и всё, конечно же проблема до сих пор возникает "как получить эту библиотеку, если зловред уже работает", т.е. он может спокойно подменить require , package и т.д. , т.е. даже если библиотека загружена, ты не можешь удостовериться в том, что это правда она, однако то, что сама таблица защищена уже гарантирует, что те, кто получил её не обнаружат сюрприз. Как фиксить такое фз, и это уже просто чисто для размышлений (но было бы неплохо, если можно было подтвердить). Было бы неплохо, если сам код был под спойлером в теге "код". Как знаешь, в таблицах, которые хочешь защитить могут находиться также и другие таблицы. Иногда хочется защитить и их (но не всегда), поэтому хотелось бы видеть функцию, которая будет рекурсивно защищать все таблицы от изменений, которые есть в защищаемой таблице. (не забудь про возможность зацикливания, например в защищаемой таблице может вдруг оказаться ссылка на её саму). Ну и на последок, то, что в getmetatable от зеркала возвращается строка защитная, не очень хорошо, иногда хотелось бы видеть метатаблицу, оригинальной (конечно же readonly), тут тогда возникает вопрос, до какой глубины идти?, ответ: до тех пор, пока есть метатаблица у метатаблицы и она ещё не защищена (в кратце расширение к рекурсивной защите). В принципе это будет создавать дополнительные нагрузки на память, поэтому можно поставить флажок, нужно ли так делать. Вроде бы большего ожидать от этой библиотеки смысла скорее всего нету (до тех пор, пока она касается только защиты таблиц). А за старания респект. UPD: короче, нужен багфикс сего чуда Показать содержимое Если вкратце, то при обращении к метаметодам зеркала получается, что оно берёт сырые значения из метатаблицы (вот как раз таки причина, почему у меня не удалась та атака с __add , ну да ладно, она хотя бы показала, что в теории любая глобальная переменная может быть злом), а это может оказаться довольно довольно важной вещью таблицы (т.е. нужно сделать будет редирект всех метаметодов, либо изменить структуру работы). Функция protect вместе с зеркалом таблицы возвращает метатаблицу зеркала (на самом деле нет, но она косвенно влияет на настоящую метатаблицу зеркала). Библиотеку нужно подгружать в начале работы системы! Можно вот такой код поместить в /boot/: require"tprotect" package.loaded.component = tprotect.protect(component) package.loaded.computer = tprotect.protect(computer) package.loaded.unicode = tprotect.protect(unicode) do local loaded=package.loaded local unloadable={component=true,computer=true,unicode=true,tprotect=true,filesystem=true} local raw_type=type function package.unload(name) assert(raw_type(name)=="string","bad argument #1 to package.unload (string expected, got "..raw_type(name)..")") if not unloadable[name] then package.loaded[name]=nil end end end package.loaded=tprotect.protect(package.loaded) _G.package=tprotect.protect(package) И постараться чтобы этот бутскрипт выполнился последним (предпологается, что другие бутскрипты хорошие и не вредные!). Еще можно реализовать права на файлы, как в *nix, но для этого я и пишу Kion (это ОСь такая). Чудо щас исправлю Изменено 26 января, 2019 пользователем Kimapr Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
astral17 65 Опубликовано: 26 января, 2019 6 минут назад, Kimapr сказал: Функция protect вместе с зеркалом таблицы возвращает метатаблицу зеркала (на самом деле нет, но она косвенно влияет на настоящую метатаблицу зеркала). Библиотеку нужно подгружать в начале работы системы! Можно вот такой код поместить в /boot/: require"tprotect" package.loaded.component = tprotect.protect(component) package.loaded.computer = tprotect.protect(computer) package.loaded.unicode = tprotect.protect(unicode) do local loaded=package.loaded local unloadable={component=true,computer=true,unicode=true,tprotect=true,filesystem=true} local raw_type=type function package.unload(name) assert(raw_type(name)=="string","bad argument #1 to package.unload (string expected, got "..raw_type(name)..")") if not unloadable[name] then package.loaded[name]=nil end end end package.loaded=tprotect.protect(package.loaded) _G.package=tprotect.protect(package) И постараться чтобы этот бутскрипт выполнился последним (предпологается, что другие бутскрипты хорошие и не вредные!). Еще можно реализовать права на файлы, как в *nix, но для этого я и пишу Kion (это ОСь такая). Чудо щас исправлю Если сделать package.loaded readonly то вся система сломается к чертям, т.к. не сможет загружать новые библиотеки и т.д. и т.п., ещё проблемка в том, что нужно в таком случае защищать и _G ведь кто мешает подменить package на другую таблицу?, затем кто мешает подменить функцию require ?, заменить require на костыль, который вместо tprotect вернёт заражённую копию?, так что тут как вариант добавить readonly поля по выбору, затем функция не возвращает метатаблицу зеркала, а возвращает метатаблицу оригинала. Когда говорил, что иногда хотелось бы видеть метатаблицы, я имел ввиду через getmetatable . Ну а вообще, для другой системы может и будет внутренняя защита от таких вещей, но это нужно смотреть на то, какая структура будет. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Kimapr Автор темы 10 Опубликовано: 26 января, 2019 (изменено) 1 час назад, astral17 сказал: Если сделать package.loaded readonly то вся система сломается к чертям, т.к. не сможет загружать новые библиотеки и т.д. и т.п., ещё проблемка в том, что нужно в таком случае защищать и _G ведь кто мешает подменить package на другую таблицу?, затем кто мешает подменить функцию require ?, заменить require на костыль, который вместо tprotect вернёт заражённую копию?, так что тут как вариант добавить readonly поля по выбору, затем функция не возвращает метатаблицу зеркала, а возвращает метатаблицу оригинала. Когда говорил, что иногда хотелось бы видеть метатаблицы, я имел ввиду через getmetatable . Ну а вообще, для другой системы может и будет внутренняя защита от таких вещей, но это нужно смотреть на то, какая структура будет. Я такое в Kion в init поставил и все работает нормально. У библы package есть свой оригинал package.loaded, и он хранится в локальной переменной. Посмотри на код из /lib/package.lua: local loaded = { ["_G"] = _G, ["bit32"] = bit32, ["coroutine"] = coroutine, ["math"] = math, ["os"] = os, ["package"] = package, ["string"] = string, ["table"] = table } package.loaded = loaded А защита _G сломает некоторые программы к чертям. В _G нельзя будет писать, но это ещё не всё! Просто переопределив _G _G=tprotect.protect(_G) Ничего не получится. Защищена будет только сама переменная, но не таблица глобального окружения. Её нужно будет менять для каждой прожки. В таком случае лучше каждой программе сделать свое глобальное окружение. Это будет легче. А там пусть какую-угодно фигню творят, лишь бы out of memory не было (при должной защите, это единственный краш, который программа сможет нанести системе). Изменено 26 января, 2019 пользователем Kimapr Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Kimapr Автор темы 10 Опубликовано: 26 января, 2019 (изменено) И вообще, какого чёрта мы опустились до старта системы? Я вообще просто библиотечку для защиты таблиц сделал, а как её использовать - не моё дело (если я не являюсь пользователем, при разработке Kion - я являюсь им) Изменено 26 января, 2019 пользователем Kimapr Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
astral17 65 Опубликовано: 26 января, 2019 (изменено) 51 минуту назад, Kimapr сказал: Я такое в Kion в init поставил и все работает нормально. У библы package есть свой оригинал package.loaded, и он хранится в локальной переменной. Посмотри на код из /lib/package.lua: local loaded = { ["_G"] = _G, ["bit32"] = bit32, ["coroutine"] = coroutine, ["math"] = math, ["os"] = os, ["package"] = package, ["string"] = string, ["table"] = table } package.loaded = loaded А защита _G сломает некоторые программы к чертям. В _G нельзя будет писать, но это ещё не всё! Просто переопределив _G _G=tprotect.protect(_G) Ничего не получится. Защищена будет только сама переменная, но не таблица глобального окружения. Её нужно будет менять для каждой прожки. В таком случае лучше каждой программе сделать свое глобальное окружение. Это будет легче. А там пусть какую-угодно фигню творят, лишь бы out of memory не было (при должной защите, это единственный краш, который программа сможет нанести системе). На счёт сломается система, запускал на OpenOS 1.7.2 в boot tprotect не становился глобальной переменной, поэтому RE был, ок, заменил первую строчку на tprotect = require ... , система зависла и отказалась загружаться. Ок попробуем не из boot запустить, а как обычный файл..., ммм эпик фэйл, команды консоли сломались, можно было идти глубже но зачем, ведь предложенные строчки для другой системы (а вообще, хоть у package есть оригинал на себя, у него не хранится отдельно оригинал на package.loaded , который становится readonly , но это опустим). Предположим, что это сработало, никто до сих пор не мешает подменить функцию require и при вызове require("tprotect") заменить на своё. Или заменить _G.package на другую таблицу. Именно из-за таких проблем я и написал 6 часов назад, astral17 сказал: и это уже просто чисто для размышлений И это также отвечает на это: 44 минуты назад, Kimapr сказал: И вообще, какого чёрта мы опустились до старта системы? Я вообще просто библиотечку для защиты таблиц сделал, а как её использовать - не моё дело (если я не являюсь пользователем, при разработке Kion - я являюсь им) Можно и дальше перечислять проблемы, но раз меня критикуют, я пожалуй откажусь. Однако основная мысль прошлого сообщения была в добавке возможности защиты частичной таблиц, т.е. чтобы только некоторые поля были readonly , что позволило бы юзеру с гораздо меньшими усилиями защититься от всего перечисленного выше. Однако хозяин - барин, автор библиотеки не я, поэтому и не я выбираю, что в ней будет, а только предлагаю. Изменено 26 января, 2019 пользователем astral17 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Kimapr Автор темы 10 Опубликовано: 26 января, 2019 А можно скриншот ошибки? Я патчил OpenOS 1.7.3, изменив /lib/core/boot.lua: ... local package = dofile("/lib/package.lua") local tprotect = dofile("/lib/tprotect.lua") do -- Unclutter global namespace now that we have the package module and a filesystem _G.component = nil _G.computer = nil _G.process = nil _G.unicode = nil -- Inject the package modules into the global namespace, as in Lua. _G.package = package -- Initialize the package module with some of our own APIs. package.loaded.tprotect = tprotect package.loaded.component = component package.loaded.computer = computer package.loaded.unicode = unicode package.loaded.buffer = dofile("/lib/buffer.lua") package.loaded.filesystem = dofile("/lib/filesystem.lua") package.loaded.color = dofile("/lib/color.lua") -- Inject the io modules _G.io = dofile("/lib/io.lua") end ... status("Initializing system...") package.loaded.component = tprotect.protect(component) package.loaded.computer = tprotect.protect(computer) package.loaded.unicode = tprotect.protect(unicode) do local loaded=package.loaded local unloadable={component=true,computer=true,unicode=true,tprotect=true,filesystem=true} local raw_type=type function package.unload(name) assert(raw_type(name)=="string","bad argument #1 to package.unload (string expected, got "..raw_type(name)..")") if not unloadable[name] then package.loaded[name]=nil end end end package.loaded=tprotect.protect(package.loaded) _G.package=tprotect.protect(package) computer.pushSignal("init") -- so libs know components are initialized. require("event").pull(1, "init") -- Allow init processing. _G.runlevel = 1 if gpu and screen then gpu.setResolution(gpu.maxResolution()) end Здесь после всех бутскриптов запускается процесс защиты дефолтных либ OpenComputers, а также package. Сам tprotect уже себя защитил. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
astral17 65 Опубликовано: 26 января, 2019 (изменено) ммм, у нас походу немного разное понятие boot было, я подумал, что кинуть файл в /boot/*.lua , но вижу был не прав. Однако согласен, что я не заметил, что package.loaded присваивается от локальной и от неё работает, в этом моя ошибка. Однако это остаётся актуальным. 1 час назад, astral17 сказал: Предположим, что это сработало, никто до сих пор не мешает подменить функцию require и при вызове require("tprotect") заменить на своё. Или заменить _G.package на другую таблицу. Замена _G.package может сработать против тех, кто не доверяет require и идёт напрямик. При изменении /lib/core/boot.lua всё нормально запустилось. UPD: И если что, я не говорю, что именно ты должен сделать эту защиту, я говорю, что ты должен помочь тем, кто захочет это сделать, ведь использовать набор методов из библиотеки гораздо проще, чем написать её с нуля Цитата Однако основная мысль прошлого сообщения была в добавке возможности защиты частичной таблиц, т.е. чтобы только некоторые поля были readonly , что позволило бы юзеру с гораздо меньшими усилиями защититься от всего перечисленного выше. Однако хозяин - барин, автор библиотеки не я, поэтому и не я выбираю, что в ней будет, а только предлагаю. Изменено 26 января, 2019 пользователем astral17 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Kimapr Автор темы 10 Опубликовано: 26 января, 2019 (изменено) 45 минут назад, astral17 сказал: ммм, у нас походу немного разное понятие boot было, я подумал, что кинуть файл в /boot/*.lua , но вижу был не прав. Нет, я действительно говорил про /boot/, но сам я сделал через /lib/core/boot.lua. Цитата При изменении /lib/core/boot.lua всё нормально запустилось. Помоему если уж ты и ставишь свой скрипт в /boot/, ты должен назвать его так, чтобы он выполнялся последним, т.е. ПОСЛЕ других бутскриптов. Например, название "zz_tprotect_init.lua" прекрасно подойдет, т.к. z - последняя (или одна из таковых) буква английского алфавита. Цитата Однако основная мысль прошлого сообщения была в добавке возможности защиты частичной таблиц, т.е. чтобы только некоторые поля были readonly , что позволило бы юзеру с гораздо меньшими усилиями защититься от всего перечисленного выше. Однако хозяин - барин, автор библиотеки не я, поэтому и не я выбираю, что в ней будет, а только предлагаю. В своей библиотеке я нарочно допустил брешь, так что если в метатаблице защищаемой таблицы (не зеркальной!) объявлена функция __newindex, то вместо ошибки будет выполнена она. Учитывая это, в __newindex можно реализовать защиту некоторых полей тем, что если их ключ совпадает с поданным в функцию ключём, то ничего не делать, или кинуть эррор. Изменено 26 января, 2019 пользователем Kimapr Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
ArtHacker 38 Опубликовано: 5 июля, 2019 (изменено) @Kimapr извини за упоминание. В последней версии Kion которая доступна в репозитории, TProtect ломает help (man) Фото: https://ibb.co/NL1Spcx Загрузить сюда не смог фото)) Исправишь? (это мой первый пост на этом форуме) Edit: Кстати, если потом ещё раз вести man то он вообще не будет работать Изменено 5 июля, 2019 пользователем ArtHacker Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
astral17 65 Опубликовано: 6 июля, 2019 @ArtHacker ну, там короче при загрузке защищается список таблиц, куда входит и os , однако либа filesystem не полностью загружается при загрузке из-за package.delay, например файлик boot/02_os.lua содержит в самом конце строчку require("package").delay(os, "/lib/core/full_filesystem.lua") а файл boot/90_filesystem.lua эту require("package").delay(fs, "/lib/core/full_filesystem.lua") ну, а теперь как именно работает эта функция, после её использования устанавливается временная метатаблица, которая после первого обращения к любому индексу этой таблицы самоудаляется и запускает файл, который указан вторым аргументом. Проблема возникает из-за того, что таблица os защищается (т.е. скрипт, который запустит full_filesystem стирается до своего запуска), а filesystem нет. Фиксить эту проблему можно перед защитой искусственно обратиться к какому-то рандомному индексу проблемных таблиц, хотя, очевидно, что такой метод это дикий костыль, хоть и простой в реализации. 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах