Перейти к содержимому
Kimapr

Защитник таблиц: tprotect

Рекомендуемые сообщения

Бывали ли у вас когда-нибудь такие ситуации, когда необходимо защитить какую-либо таблицу от записи? Например, вы пишите операционную систему в 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, ставьте "зеркальную" таблицу

 

Изменено пользователем Kimapr
bugzz detectedd

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Позволю себе немножко критики, как человеку который любит играться в сфере ИБ.

  1. math.random - это псевдорандомное число, оно уязвимо.
    Цитата

    local function newKey()
      return {math.random(10000,99999)}
    end

     

  2. Такого рода ключ можно сбрутфорсить, причем сделается это довольно быстро.
  3. Делать что-то для безопасности в скриптовой ОСи чуть менее чем бессмысленно, как по мне - весь код, даже если его обфусцировать, читается на раз два.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Не отрицая сути (ключ от таблицы нужно как минимум где-то хранить, что создает новую точку уязвимости), тем не менее был бы очень рад увидеть практическую имплементацию брутфорса пароля.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
9 часов назад, kcalBxoF сказал:

Такого рода ключ можно сбрутфорсить, причем сделается это довольно быстро.

 

Здесь ключ - это не число, а таблица с уникальным адресом. По факту для подобного даже рандом не нужен, можно было вообще возвращать пустую таблицу или функцию, служащую идентификатором для разблокировки.

 

А поскольку debug в опенкомпах крайне лимитирован, и доступа к локальным переменным нет, то и выпотрошить весь стек оперативы в поисках ключа для брутфорса не получится.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
7 часов назад, ECS сказал:

 

Здесь ключ - это не число, а таблица с уникальным адресом.

А этот момент я упустил, т.к в код не особо вчитывался, думал что используется именно число из этого рандома.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
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, она не защищена, т.е. её можно спокойно изменять и мы видим спокойно все её значения

Скрытый текст

RhMnCyn.png

снимаем защиту на изменение 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 вообще под чистую сломана. Ну надеюсь в следующей версии автор учтёт хотя бы часть написанного тута, и следующая версия библиотеки будет лучше.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
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. Вызывайте её, когда вам надоест защита вашей таблицы или когда вы захотите сменить метатаблицу.

 

Новая версия идет! Переписано с нуля, теперь больше безопасности и меньше нерабочести! Ну и сложности тоже поменьше. А то как то сложновато.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Дубль 2, короче сдаюсь в этот раз получилось гораздо качественнее, уязвимости есть, но не очень критические и легко фиксятся.

 

KbtxmIv.png

Так сказать защита rawset не очень хороша), а если точнее, то error не обязательно выходит из функции, например если сделать так, как на скрине, то сразу понятно, что код продолжит работу. Т.е. нужно либо хранить персональный error , либо дописать везде return nil или что-то похожее), похожая проблема с getmetatable и setmetatable , т.е. если их переписать, то получится, что мы сможем выхукнуть таблицы в момент их защиты.

Также это дело касается type , т.к. я могу обломать валидацию mto на таблицу и запихнуть через __metatable функцию, т.е. tcopy_mt = mto = myfunction , а если __index это функция, то при обращении к метатаблице я смогу получить и саму чистую метатаблицу, т.е. mt, а сама она никак не защищена, как сиё сделать, (цель вообще вытянуть protectid) , смотрим, нам нужно обратиться в mt к значению, которого нету например _add , а значит вызывается __index в который помещается mt, "__add" и тут мы и хукаем mt. (как вытянуть не проверялось, и писалось почти с закрытыми глазами, так что скорее всего сиё чудо только в теории, завтра чекну)

Изменено пользователем astral17

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Проблематичненько! Я даже не заметил, что можно их переопределить, как это сделал я. Шас исправим-с.

Вот патч:

local getmetatable=getmetatable -- 
local setmetatable=setmetatable --
local type=type                 -- Дополнительная зашыта
local error=error               --
local assert=assert             --

Библиотека все еще уязвима к подмене этих функций до её загрузки. Так что её нужно загружать до загрузки чего-либо ещё опасного

Изменено пользователем Kimapr

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Ну, из уязвимостей вроде бы и всё, конечно же проблема до сих пор возникает "как получить эту библиотеку, если зловред уже работает", т.е. он может спокойно подменить require , package и т.д. , т.е. даже если библиотека загружена, ты не можешь удостовериться в том, что это правда она, однако то, что сама таблица защищена уже гарантирует, что те, кто получил её не обнаружат сюрприз. Как фиксить такое фз, и это уже просто чисто для размышлений (но было бы неплохо, если можно было подтвердить).

Было бы неплохо, если сам код был под спойлером в теге "код".

Как знаешь, в таблицах, которые хочешь защитить могут находиться также и другие таблицы. Иногда хочется защитить и их (но не всегда), поэтому хотелось бы видеть функцию, которая будет рекурсивно защищать все таблицы от изменений, которые есть в защищаемой таблице. (не забудь про возможность зацикливания, например в защищаемой таблице может вдруг оказаться ссылка на её саму).

Ну и на последок, то, что в getmetatable от зеркала возвращается строка защитная, не очень хорошо, иногда хотелось бы видеть метатаблицу, оригинальной (конечно же readonly), тут тогда возникает вопрос, до какой глубины идти?, ответ: до тех пор, пока есть метатаблица у метатаблицы и она ещё не защищена (в кратце расширение к рекурсивной защите). В принципе это будет создавать дополнительные нагрузки на память, поэтому можно поставить флажок, нужно ли так делать.

Вроде бы большего ожидать от этой библиотеки смысла скорее всего нету (до тех пор, пока она касается только защиты таблиц). А за старания респект.

UPD:
короче, нужен багфикс сего чуда

pLReZSn.png

Если вкратце, то при обращении к метаметодам зеркала получается, что оно берёт сырые значения из метатаблицы (вот как раз таки причина, почему у меня не удалась та атака с __add , ну да ладно, она хотя бы показала, что в теории любая глобальная переменная может быть злом), а это может оказаться довольно довольно важной вещью таблицы (т.е. нужно сделать будет редирект всех метаметодов, либо изменить структуру работы).

Изменено пользователем astral17

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
3 часа назад, astral17 сказал:

Ну, из уязвимостей вроде бы и всё, конечно же проблема до сих пор возникает "как получить эту библиотеку, если зловред уже работает", т.е. он может спокойно подменить require , package и т.д. , т.е. даже если библиотека загружена, ты не можешь удостовериться в том, что это правда она, однако то, что сама таблица защищена уже гарантирует, что те, кто получил её не обнаружат сюрприз. Как фиксить такое фз, и это уже просто чисто для размышлений (но было бы неплохо, если можно было подтвердить).

Было бы неплохо, если сам код был под спойлером в теге "код".

Как знаешь, в таблицах, которые хочешь защитить могут находиться также и другие таблицы. Иногда хочется защитить и их (но не всегда), поэтому хотелось бы видеть функцию, которая будет рекурсивно защищать все таблицы от изменений, которые есть в защищаемой таблице. (не забудь про возможность зацикливания, например в защищаемой таблице может вдруг оказаться ссылка на её саму).

Ну и на последок, то, что в getmetatable от зеркала возвращается строка защитная, не очень хорошо, иногда хотелось бы видеть метатаблицу, оригинальной (конечно же readonly), тут тогда возникает вопрос, до какой глубины идти?, ответ: до тех пор, пока есть метатаблица у метатаблицы и она ещё не защищена (в кратце расширение к рекурсивной защите). В принципе это будет создавать дополнительные нагрузки на память, поэтому можно поставить флажок, нужно ли так делать.

Вроде бы большего ожидать от этой библиотеки смысла скорее всего нету (до тех пор, пока она касается только защиты таблиц). А за старания респект.

UPD:
короче, нужен багфикс сего чуда

 

  Показать содержимое

pLReZSn.png

 

Если вкратце, то при обращении к метаметодам зеркала получается, что оно берёт сырые значения из метатаблицы (вот как раз таки причина, почему у меня не удалась та атака с __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 (это ОСь такая). Чудо щас исправлю

Изменено пользователем Kimapr

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
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 . Ну а вообще, для другой системы может и будет внутренняя защита от таких вещей, но это нужно смотреть на то, какая структура будет.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
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 не было (при должной защите, это единственный краш, который программа сможет нанести системе).

Изменено пользователем Kimapr

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

И вообще, какого чёрта мы опустились до старта системы? Я вообще просто библиотечку для защиты таблиц сделал, а как её использовать - не моё дело (если я не являюсь пользователем, при разработке Kion - я являюсь им)

Изменено пользователем Kimapr

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
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 , что позволило бы юзеру с гораздо меньшими усилиями защититься от всего перечисленного выше. Однако хозяин - барин, автор библиотеки не я, поэтому и не я выбираю, что в ней будет, а только предлагаю.

 

Изменено пользователем astral17

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

А можно скриншот ошибки? Я патчил 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 уже себя защитил.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

ммм, у нас походу немного разное понятие boot было, я подумал, что кинуть файл в /boot/*.lua , но вижу был не прав. Однако согласен, что я не заметил, что package.loaded присваивается от локальной и от неё работает, в этом моя ошибка. Однако это остаётся актуальным.

1 час назад, astral17 сказал:

Предположим, что это сработало, никто до сих пор не мешает подменить функцию require и при вызове require("tprotect") заменить на своё. Или заменить _G.package на другую таблицу.

Замена _G.package может сработать против тех, кто не доверяет require и идёт напрямик. При изменении /lib/core/boot.lua всё нормально запустилось.

UPD:
И если что, я не говорю, что именно ты должен сделать эту защиту, я говорю, что ты должен помочь тем, кто захочет это сделать, ведь использовать набор методов из библиотеки гораздо проще, чем написать её с нуля

Цитата

Однако основная мысль прошлого сообщения была в добавке возможности защиты частичной таблиц, т.е. чтобы только некоторые поля были readonly , что позволило бы юзеру с гораздо меньшими усилиями защититься от всего перечисленного выше. Однако хозяин - барин, автор библиотеки не я, поэтому и не я выбираю, что в ней будет, а только предлагаю.

 

Изменено пользователем astral17

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
45 минут назад, astral17 сказал:

ммм, у нас походу немного разное понятие boot было, я подумал, что кинуть файл в /boot/*.lua , но вижу был не прав.

Нет, я действительно говорил про /boot/, но сам я сделал через /lib/core/boot.lua.

Цитата

При изменении /lib/core/boot.lua всё нормально запустилось.

Помоему если уж ты и ставишь свой скрипт в /boot/, ты должен назвать его так, чтобы он выполнялся последним, т.е. ПОСЛЕ других бутскриптов. Например, название

"zz_tprotect_init.lua" прекрасно подойдет, т.к. z - последняя (или одна из таковых) буква английского алфавита.

Цитата

Однако основная мысль прошлого сообщения была в добавке возможности защиты частичной таблиц, т.е. чтобы только некоторые поля были readonly , что позволило бы юзеру с гораздо меньшими усилиями защититься от всего перечисленного выше. Однако хозяин - барин, автор библиотеки не я, поэтому и не я выбираю, что в ней будет, а только предлагаю.

В своей библиотеке я нарочно допустил брешь, так что если в метатаблице защищаемой таблицы (не зеркальной!) объявлена функция __newindex, то вместо ошибки будет выполнена она. Учитывая это, в __newindex можно реализовать защиту некоторых полей тем, что если их ключ совпадает с поданным в функцию ключём, то ничего не делать, или кинуть эррор.

Изменено пользователем Kimapr

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

@Kimapr извини за упоминание.

В последней версии Kion которая доступна в репозитории, TProtect ломает help (man)

Фото: https://ibb.co/NL1Spcx

Загрузить сюда не смог фото))

Исправишь?

(это мой первый пост на этом форуме)

Edit:

Кстати, если потом ещё раз вести man то он вообще не будет работать

 

 

Изменено пользователем ArtHacker

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

@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 нет. Фиксить эту проблему можно перед защитой искусственно обратиться к какому-то рандомному индексу проблемных таблиц, хотя, очевидно, что такой метод это дикий костыль, хоть и простой в реализации.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в тему...

×   Вы вставили отформатированное содержимое.   Удалить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отобразить как ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.


×
×
  • Создать...