Что такое модификация файла
В операционной системе UNIX файл является хранилищем двоичных и символьных данных, хранимых как поток байтов. ВUNIX символьные данные кодируются с помощью кода ASCII, хотя на таких системах, как мэйнфрейм IBM 390, используется кодировка EBCDIC. Коды ASCII и EBSDICотличаются друг от друга, т.е. один и тот же код в них соответствует разным символам, а один и тот же символ закодирован в них разными кодами. В разных операционных системах данные хранятся по-разному. Данное обстоятельство может вызвать проблемы при попытке в одной операционной системе обработать данные, созданные в другой операционной системе. Необходимы специальные программы для конвертирования данных из файлов, созданных в одной операционной системе в файлы другой операционной системы так, чтобы они были пригодны для обработки.
Файлы содержат разные типы информации. Например, файл может содержать исходный код программы на С, COBOL или C++, он может быть текстовым документом с письмом от друга или исполняемым модулем программы. В UNIXсуществует несколько "родных" форматов файлов, которые можно просматривать или копировать, используя команды системы. Однако некоторые файлы нельзя обработать внутренними командами UNIX. Например, файлы базы данных для СУБД независимых разработчиков, таких как Oracle, требуют для обработки специальных программ.
Файл может располагаться на разных носителях. Файлы бывают постоянными, т.е. записанными на диске или временными -в памяти; данные из файла могут выводиться на терминал, или файл может принимать данные с терминала. Если файл постоянный, то его можно просмотреть, а если файл временный, то вы можете даже не знать о его существовании.
Функции, которые обычно выполняются по отношению к файлу, таковы:
§ открытие файла для обработки
§ чтение данных из файла для обработки
§ запись данных в файл после обработки
§ закрытие файла после того, как вся необходимая обработка выполнена
Теперь,когда у вас есть представление о том, что такое файл и что он содержит, пришло время узнать побольше о тех разных типах файлов, с которыми вы можете столкнуться.
Типы файлов
Данный раздел содержит обсуждение различных типов файлов, имеющихся в UNIX. С некоторыми из данных типов файлов вы наверняка уже знакомы, например с текстовыми документами и с файлами исходных кодов.
Регулярные файлы
Регулярные файлы -это те файлы, с которыми вы, вероятно, знакомы лучше всего. Сущность их неизменна.Они постоянны по природе и содержат такие данные, как код программы, почту,полученную от босса, или письмо, которое вы пишете другу. Данные файлы почти всегда содержат текстовую информацию. В таких файлах данные организованы в записи. Если бы, к примеру, данная книга была файлом, содержащим данные об операционной системе UNIX, то каждая строка в книге была бы записью.
Как UNIX узнает о записях? Существует специальный символ, называющийся символом новой строки (newline character), который используется UNIX для поиска места, где одна запись заканчивается, а другая начинается. Многие команды UNIX поддерживают обработку текстов. Однако, имейте в виду, что текстовые файлы -не единственный тип регулярных файлов. Некоторые файлы представляют собой поток байтов без каких-либо символов новой строки. Хотя UNIX изначально была предназначена для обработки текстовых документов, она не сможет обработать такие файлы.
Ниже следуют примеры нескольких регулярных файлов:
Приведенные здесь примеры следуют обычным для UNIX соглашениям по менованию Однако это просто соглашения, а не правила. Поэтому, файл можно назвать и prog.с даже если он содержит письмо к боссу.
Вот пример списка атрибутов, имеющихся у файла. Файл называетсяtestfile, и атрибуты выводятся с использованием следующей команды:
UNIX отслеживает атрибуты, используя структуру данных под названием i-node (индексный дескриптор), Каждый индексный дескриптор в системе идентифицируется числом, которое называется номером индексного дескриптора. Каждый файл в системе имеет ассоциированный с ним индексный дескриптор, который содержит следующую информацию:
Для временной информации в индексном дескрипторе выделено несколько полей:
Время последнего доступа меняется при выполнении любой операции с файлом. Дата последней модификации изменяется при модификации файла. Время последней модификации индексного дескриптора меняется тогда, когда меняется информация, хранящаяся в индексном дескрипторе.
Дополнительные настройки (Patches)
Максимальное число одновременных соединений. Файл tcpip.sys в Service Pack 2 для Windows XP ограничивает максимальное число одновременных незавершенных (half-open) соединений (то есть, когда запрос на соединение отправлен, но ответ еще не получен). Если число таких "полу-соединений" превышает максимум, то они помещаются в очередь. Обычно это оказывает негативный эффект на скорость работы файлообменных (P2P) сетей, а также некоторых специфических программ типа сканера ресурсов локальной сети LanScope. Так или иначе, число одновременных незавершенных соединений в каждую секунду не может превышать десяти. Лимит, видимо, введен из соображений безопасности. Если у вас нет насущной необходимости в большем количестве соединений, то лучше оставьте файл в покое. Впрочем, как я сказал выше, иногда просто необходимо обойти лимит. Вы также можете загрузить уже измененные файл tcpip.sys по прямой ссылке с нашего сайта: Windows XP SP2 (171 kb) Достаточно просто скопировать файл в директорию i386 вашего дистрибутива и согласиться на перезапись.
Частота работы портов USB. Модификация файла позволяет добиться более плавного перемещения курсора у USB мышей отдельных производителей.
Если вы действительно хотели заменить файл, то простоe нажатие Cancel решает проблему. А что если вы не хотите каждый раз жать Cancel или защищенный файл уже находится в директории dllcache? Вот в таком случае sfc_os.dll и придется модифицировать.
Твики (Tweaks)
Твики реестра, надеюсь, будут понятны и без дополнительных комментариев. Правильность русского перевода я не оценивал, смотрите сами.
Создание ISO (Create Bootable ISO)
Последовательность действий, требуемых для модификации уже существующего файла, несколько отличается от той, которая необходима для записи в файл или чтения из него. Прежде всего файл должен быть открыт для модификации (т.е. для чтения и записи одновременно). Для этой цели служит предикат openmodify.
успешен только в том случае, если файл уже присутствует на диске. Другими словами, файл должен быть создан при помощи предиката openwrite. При этом следует, конечно, помнить, что openwrite создает новый файл даже тогда, когда файл с таким именем уже имеется, затирая при этом все, что в нем находилось.
Содержимое файла можно рассматривать как поток символов. Каждый символ находится в файле на какой-то позиции. Позиция определяется удаленностью от первого символа файла. Таким образом, первый символ в файле занимает нулевую позицию, второй — первую и т. д. Вообразим себе теперь невидимый указатель, который можно сместить на любую позицию файла. Этот указатель определяет позицию, из которой будет прочитан или в которую будет записан очередной символ.
Когда файл открывается для модификации, указатель помещается в начало файла. Турбо-Пролог позволяет смещать указатель при помощи предиката filepos, который будет подробно рассмотрен в разделе, посвященном файлам прямого доступа. В настоящий момент вам требуется знать лишь то, где будет располагаться указатель после вызова openmodify.
Модификацию файла можно разбить на следующие шаги:
1. Открытие файла
2. Переадресация вывода в файл
3. Запись в файл новых данных.
4. Использование произвольных предикатов и правил, отвечающих целям программы.
5. Закрытие файла
Примером может служить
Дозапись в конец уже существующего файла
Возможность записать новые данные в конец уже существующего файла обеспечивается в Турбо-Прологе предикатом openappend. Когда файл открывается для дозаписи, указатель файла смещается в его конец.
Следующие шаги необходимо предпринять для добавления новых данных в конец файла:
1. Открытие файла
2. Переадресация вывода в файл
3. Дозапись в файл новых данных при помощи соответствующих правил.
4. Использование произвольных предикатов и правил, отвечающих целям программы.
5. Закрытие файла
Разработать программу для формирования базы данных с клавиатуры и записи в файл. После ввода слова «list», вывести все содержимое файла на экран (таблица.5).
Таблица 5
Вариант | Предметная область | Количество объектов | Количество параметров объекта |
Города | |||
Автомобили | |||
Компьютеры | |||
Дома | |||
Самолеты | |||
Страны | |||
Студенты | |||
Партии | |||
Сотрудники | |||
Вакансии | |||
Операционные системы | |||
Книги | |||
Телефонный справочник | |||
Предприятия | |||
Автобусные маршруты |
Лабораторная работа № 6
Структуры
Структуры данных вместе с сопоставлением, автоматическими возвратами и арифметикой представляют собой мощный инструмент программирования.
База данных может быть представлена на Прологе в виде множества фактов. Например, в базе данных о семьях каждая семья может описываться одним предложением. На рис. 28 показано, как информацию о каждой семье можно представить в виде структуры.
Каждая семья состоит из трех компонент: мужа, жены и детей.
[членсемьи(знн, фокc, дата(9, май, 1951),
[членсемьи(пат, фокc, дата(5, май, 1973),
членсемьи(джим, фокc, дата(5, май, 1973),
Рис. 28.Структурированная информация о семье
Тогда база данных будет состоять из последовательности фактов, подобных этому, и описывать все семьи, представляющие интерес для нашей программы. ; В действительности Пролог очень удобен для извлечения необходимой информации из такой базы данных. Здесь можно ссылаться на объекты, не указывая в деталях всех их компонент. Можно задавать только структуру интересующих вас объектов и оставлять конкретные компоненты без точного описания или лишь с частичным описанием. На рис. 29 приведено несколько примеров. Так, в запросах к базе данных можно ссылаться на всех Армстронгов с помощью терма
Символы подчеркивания обозначают различные анонимные переменные, значения которых нас не заботят. Далее можно сослаться на все семьи с тремя детьми при помощи терма:
Чтобы найти всех замужних женщин, имеющих по крайней мере троих детей, можно задать вопрос:
Главным моментом в этих примерах является то, что указывать интересующие нас объекты можно не только по их содержимому, но и по их структуре. Мы задаем одну структуру и оставляем ее аргументы в виде слотов (пропусков).
Рис. 29. Описания объектов по их структурным свойствам;
а — любая семья Армстронгов; б — любая семья, имеющая ровно трех детей; с — любая семья, имеющая по крайней мере три ребенка. Структура (с) дает возможность получить имя в фамилию жены конкретизацией переменных Имя и Фамилия
Можно создать набор процедур, который служил бы утилитой, делающей взаимодействие с нашей базой данных более удобным. Такие процедуры являлись бы частью пользовательского интерфейса. Вот некоторые полезные процедуры для нашей базы данных:
Последовательность действий, требуемых для модификации уже существующего файла, несколько отличается от той, которая необходима для записи в файл или чтения из него. Прежде всего файл должен быть открыт для модификации (т.е. для чтения и записи одновременно). Для этой цели служит предикат openmodify.
успешен только в том случае, если файл уже присутствует на диске. Другими словами, файл должен быть создан при помощи предиката openwrite. При этом следует, конечно, помнить, что openwrite создает новый файл даже тогда, когда файл с таким именем уже имеется, затирая при этом все, что в нем находилось.
Содержимое файла можно рассматривать как поток символов. Каждый символ находится в файле на какой-то позиции. Позиция определяется удаленностью от первого символа файла. Таким образом, первый символ в файле занимает нулевую позицию, второй — первую и т. д. Вообразим себе теперь невидимый указатель, который можно сместить на любую позицию файла. Этот указатель определяет позицию, из которой будет прочитан или в которую будет записан очередной символ.
Когда файл открывается для модификации, указатель помещается в начало файла. Турбо-Пролог позволяет смещать указатель при помощи предиката filepos, который будет подробно рассмотрен в разделе, посвященном файлам прямого доступа. В настоящий момент вам требуется знать лишь то, где будет располагаться указатель после вызова openmodify.
Модификацию файла можно разбить на следующие шаги:
1. Открытие файла
2. Переадресация вывода в файл
3. Запись в файл новых данных.
4. Использование произвольных предикатов и правил, отвечающих целям программы.
5. Закрытие файла
Примером может служить
< правила для выборочной записи в файл >,
< любые другие правила или предикаты >
Дозапись в конец уже существующего файла
Возможность записать новые данные в конец уже существующего файла обеспечивается в Турбо-Прологе предикатом openappend. Когда файл открывается для дозаписи, указатель файла смещается в его конец.
Следующие шаги необходимо предпринять для добавления новых данных в конец файла:
1. Открытие файла
2. Переадресация вывода в файл
3. Дозапись в файл новых данных при помощи соответствующих правил.
4. Использование произвольных предикатов и правил, отвечающих целям программы.
5. Закрытие файла
< любые правила для дозаписи в файл >,
< любые другие правила или предикаты >
Разработать программу для формирования базы данных с клавиатуры и записи в файл. После ввода слова «list», вывести все содержимое файла на экран (таблица.5).
Таблица 5
Вариант | Предметная область | Количество объектов | Количество параметров объекта |
Города | |||
Автомобили | |||
Компьютеры | |||
Дома | |||
Самолеты | |||
Страны | |||
Студенты | |||
Партии | |||
Сотрудники | |||
Вакансии | |||
Операционные системы | |||
Книги | |||
Телефонный справочник | |||
Предприятия | |||
Автобусные маршруты |
Лабораторная работа № 6
Структуры
Структуры данных вместе с сопоставлением, автоматическими возвратами и арифметикой представляют собой мощный инструмент программирования.
База данных может быть представлена на Прологе в виде множества фактов. Например, в базе данных о семьях каждая семья может описываться одним предложением. На рис. 28 показано, как информацию о каждой семье можно представить в виде структуры.
Каждая семья состоит из трех компонент: мужа, жены и детей.
Поскольку количество детей в разных семьях может быть разным, то их целесообразно представить в виде списка, состоящего из произвольного числа элементов. Каждого члена семьи в свою очередь можно представить структурой, состоящей из четырех компонент: имени, фамилии, даты рождения и работы. Информация о работе - это либо «не работает», либо указание места работы и оклада (дохода). Информацию о семье, изображенной на рис.28, можно занести в базу данных с помощью предложения:
[членсемьи(знн, фокc, дата(9, май, 1951),
[членсемьи(пат, фокc, дата(5, май, 1973),
членсемьи(джим, фокc, дата(5, май, 1973),
Рис. 28.Структурированная информация о семье
Тогда база данных будет состоять из последовательности фактов, подобных этому, и описывать все семьи, представляющие интерес для нашей программы. ; В действительности Пролог очень удобен для извлечения необходимой информации из такой базы данных. Здесь можно ссылаться на объекты, не указывая в деталях всех их компонент. Можно задавать только структуру интересующих вас объектов и оставлять конкретные компоненты без точного описания или лишь с частичным описанием. На рис. 29 приведено несколько примеров. Так, в запросах к базе данных можно ссылаться на всех Армстронгов с помощью терма
Символы подчеркивания обозначают различные анонимные переменные, значения которых нас не заботят. Далее можно сослаться на все семьи с тремя детьми при помощи терма:
Чтобы найти всех замужних женщин, имеющих по крайней мере троих детей, можно задать вопрос:
Главным моментом в этих примерах является то, что указывать интересующие нас объекты можно не только по их содержимому, но и по их структуре. Мы задаем одну структуру и оставляем ее аргументы в виде слотов (пропусков).
Рис. 29. Описания объектов по их структурным свойствам;
а — любая семья Армстронгов; б — любая семья, имеющая ровно трех детей; с — любая семья, имеющая по крайней мере три ребенка. Структура (с) дает возможность получить имя в фамилию жены конкретизацией переменных Имя и Фамилия
Можно создать набор процедур, который служил бы утилитой, делающей взаимодействие с нашей базой данных более удобным. Такие процедуры являлись бы частью пользовательского интерфейса. Вот некоторые полезные процедуры для нашей базы данных:
муж(X) :- /* X – муж */
жена(X) :— /* X – жена */
ребенок(X) :- /* X – ребенок */
принадлежит(X, [X | L ].
принадлежит(X, [Y | L ] :-
/*Любой член семьи в базе данных*/
Этими процедурами можно воспользоваться, например, в следующих запросах к базе данных:
Найти имена всех людей из базы данных:
- существует(членсемьи(Имя, Фамилия,_,_)).
Найти всех детей, родившихся в 1981- году:
датарождения(X, дата(_, _, 1981) ).
Найти всех работающих жен:
Найти имена и фамилия людей, которые не работают и родились до 1963 года:
- существует (членсемьи), (Имя, Фамилия,
Найти людей, родившихся до 1950 года, чей доход меньше, чем 8000:
Найти фамилии людей, имеющих по крайней мере трех детей:
Для подсчета общего дохода семьи полезно определить сумму доходов людей из некоторого списка в виде двухаргументного отношения:
Это отношение можно запрограммировать так:
общий([], 0). /* Пустой список людей */
общий([ Человек | Список], Сумма) :-
доход( Человек, S),
/* S-доход первого человека */
общий( Список, Остальные),
/*Остальные-сумма доходов остальных */
Сумма is S + Остальные.
Теперь общие доходы всех семей могут быть найдены с помощью вопроса:
- семья( Муж, Жена, Дети),
общий( [Муж, Жена | Дети], Доход).
Пусть отношение длина подсчитывает количество элементов списка. Тогда мы можем найти все семьи, которые имеют доход на члена семьи, меньший, чем 2000, при помощи вопроса:
- семья(Муж, Жена, Дети),
общий([ Муж, Жена \ Дети], Доход),
длина([ Муж, Жена | Дети], N),
Абстракция данных
Абстракцию данных можно рассматривать как процесс 1 организации различных фрагментов информации в единые логические единицы (возможно, иерархически), придавая ей при этом некоторую концептуально осмысленную форму. Каждая информационная единица должна быть легко доступна в программе. В идеальном случае все детали реализации такой структуры должны быть невидимы пользователю этой структуры. Самое главное в этом процессе — дать программисту возможность использовать информацию, не думая о деталях ее действительного представления.
Обсудим один из способов реализации этого принципа на Прологе. Рассмотрим снова пример с семьей. Каждая семья — это набор некоторых фрагментов информации. Все эти фрагменты объединены в естественные информационные единицы, такие, как «член семьи» или «семья», и с ними можно обращаться как с едиными объектами. Предположим опять, что информация о семье структурирована так же, как на рис. 117. Определим теперь некоторые отношения, с помощью которых пользователь может получать доступ к конкретным компонентам семьи, не зная деталей рис. 117. Такие отношения можно назвать селекторами, поскольку они позволяют выбирать конкретные компоненты. Имя такого отношения-селектора будет совпадать с именем компоненты, которую нужно выбрать. Отношение будет иметь два аргумента: первый — объект, который содержит компоненту, и второй - саму компоненту:
Вот несколько cелекторов для структуры семья:
муж( семья Муж, _,), Муж).
жена( семья(,Жена,), Жена).
дети(семья(_,_, СписокДетей), СписокДетей).
Можно также создать селекторы для отдельных детей семьи:
первыйребенок( Семья, Первый) :-
второйребенок( Семья, Второй) :-
Можно обобщить этот селектор для выбора N-го ребенка:
nребенок( N,Семья, Ребенок) :-
дети( Семья, СписокДетей),
n_элемент( N, СписокДетей, Ребенок)
/* N-й элемент списка */
Другим интересным объектом является «член семьи». Вот некоторые связанные с ним селекторы, соответствующие рис. 28:
фамилия(членсемьи(_, Фамилия,,_), Фамилия).
датарождения(членсемьи(__,_, Дата), Дата).
Какие преимущества мы можем получить от использования отношений-селекторов? Определив их, мы можем теперь забыть о конкретном виде структуры представления информации. Для пополнения и обработки этой информации нужно знать только имена отношений-селекторов и в оставшейся части программы пользоваться только ими. В случае, если информация представлена сложной структурой, это легче, чем каждый раз обращаться к ней в явном виде. В частности, в нашем примере с семьей пользователь не обязан знать, что дети представлены в виде списка. Например, предположим, мы хотим сказать, что, Том Фоке и Джим Фоке принадлежат к одной семье и что Джим - второй ребенок Тома Используя приведенные выше отношения -селекторы, мы можем определить двух человек, назовем их Человек1 и Человек2 и семью. Следующий список целей приводит к желаемому результату:
имя(Человек1, том), фамилия(Человек1, фокc),
/* Человек1 - Том Фокc */
имя(Человек2, джим), фамилия(Человек1, фокc),
/* Человек2 - Джим Фокс */
Использование отношений-селекторов облегчает также ипоследующую модификацию программ.
Представьте себе, что мы захотели повысить эффективность программы, изменив представление информации. Все, что нужно сделать для этого, — изменить определения отношений-селекторов, и вся остальная программа без изменений будет работать с этим новым представлением.
Задание.
Разработать базу данных с вложенной структурой, соответствующей заданной предметной области и глубины (таблица 6).
Инструментальное средство SCCS представляет собой пакет программ, который может функционировать под управлением OS типа UNIX (в том числе, CONVEX-OS и OC System V), файловая система которых не поддерживает такой атрибут, как версия.
В процессе модификации исходного файла, находящегося под управлением инструментального средства SCCS , изменение его содержимого фиксируется инструментальным средством SCCS всякий раз, когда пользователь осуществляет запись файла на магнитный диск. Таким образом текущая версия исходного файла представляет собой совокупность его текущего содержимого и информации и последовательных модификациях этого содержимого, начиная с его первой версии.
Информация о последовательных модификациях исходного файла может быть представлена в виде набора последовательных изменений его содержимого, каждому элементу которого присваивается уникальный идентификационный номер - номер версии. Например, первая версия исходного файла может иметь идентификационный номер 1.1 , а две последующие - идентификационные номера 1.2 и 1.3 соответственно. Подобный подход дает пользователю возможность хранить в файле с одним и тем же именем несколько версий его содержимого. Вместе с тем при осуществлении ссылки на этот исходный файл пользователь может специфицировать различные идентификационные номера версий и таким образом обработать последовательно несколько различных вариантов исходного файла.
При отсутствии спецификации идентификационного номера версии инструментальное средство SCCS автоматически использует последнюю версию файла.
Любой исходный файл, независимо от того, содержит он исходный текст некоторой программы или текст журнальной статьи, может быть передан под управление инструментального средства SCCS, для чего необходимо воспользоваться командой ADMIN и ввести с терминала, например, следующее:
% admin -i file s.file
NO ID KEYWORDS (cm7)
После этого оригинальный исходный файл должен быть удален из файловой системы OC UNIX
Для восстановления его содержимого (с учетом всех модификаций) достаточно ввести
В результате в текущем каталоге появится исходный файл, не находящийся под управлением инструментального средства SCCS , доступ к которому разрешен только по чтению. Для вывода содержимого исходного файла на стандартный вывод нужно набрать командную строку
Для того, чтобы осуществить модификацию (с помощью редактора текста) содержимого исходного файла, находящегося под управлением инструментального средства SCCS , необходимо ввести следующую командную строку:
В результате этого, в текущем каталоге появится файл, доступ к которому разрешен и по чтению, и по записи, представляющий собой копию оригинального исходного файла, а также будет создан рабочий файл инструментального средства SCCS ( p.file ). После того, как редактирование исходного файла завершено, необходимо сообщить об этом инструментальному средству SCCS набрав командную строку
после этого на экране появится предложение ввести комментарий:
По желанию можно ввести текст комментария. После этого инструментальное средство SCCS создает новую версию исходного файла. Команда delta автоматически увеличивает номер версии на единицу. Чтобы вывести на терминал всю последовательность осуществленных преобразований содержимого исходного файла т.е. ``историю развития" файла нужно набрать в командной строке
Ниже приведем несколько примеров работы с инструментальным средством SCCS .
Если нужно вызвать для редактирования уже существующий файл s.file версию 1.5 , нужно набрать:
% get -r1.5 -e s.file
далее вызвать редактор vi (для редактирования версии 1.5)
после окончания редактирования передать файл программе delta
Если нужно передать файл под управление инструментальному средству SCCS впервые и начать нумерацию версий не с 1.1 (по умолчанию), а с версии 5.1
% admin -r5.1 -ifile s.file
Если нужно вывести содержимое файла s.file версию 1.4 (ранее созданную) на экран
% get -r1.4 -p s.file
Например, если нужно отредактировать уже существующую версию 1.1 , то после редактирования при записи программа delta присвоит номер версии 1.1.1.1.
Рассмотренные здесь примеры демонстрируют лишь небольшую часть возможностей инструментального средства SCCS , тем не менее даже по ним можно уже получить представление об использовании этой системы.
Напомним еще раз о существующих возможностях:
- admin - выполняет управляющие функции над закодированным файлом;
- get - восстанавливает версию закодированного файла;
- delta - помещает новую версию в закодированный файл;
- prs - печатает закодированный файл.
Более полную информацию можно получить в man admin , man get , man delta .
Этичный хакинг и тестирование на проникновение, информационная безопасность
Всё началось с того, что я заинтересовался, как найти в системе файлы созданные или изменённые за определённый промежуток времени. Например, как узнать, какие файлы в папке /etc были изменены за последний день или за последнюю неделю?
С этой задачей прекрасно справляется команда find, правда выяснилось, что у каждого файла доступны следующие метки времени:
- Доступ
- Модифицирован
- Изменён
- Создан
Чтобы понимать, что именно мы нашли, нужно чётко знать разницу между этими свойствами файла.
Именно этому и посвящена данная статья, в ней будет рассказано:
- как посмотреть время изменения файла и чем время изменения отличается от времени модификации файла
- как узнать, когда файл последний раз открывали
- как узнать время создания файла в Linux
- как поменять время доступа, модификации и изменения файла.
- как поменять время создания файла в Linux и возможно ли это вообще
- как найти файлы, которые недавно были изменены или открыты
- как найти файлы, которые открывали, редактировали или создавали в определённый день или определённое время назад
Все эти вопросы имеют практический смысл: от простого домашнего использования — поиск файла, для которого мы не помним имя, но для которого помним время создания; до серьёзного профессионального — появление новых файлов на сервере, либо недавние модификации важных файлов, могут свидетельствовать о взломе системы или неавторизованном доступе, могут помочь при расследовании подобных случаев для понимания событий произошедшего. Либо быть применены с противоположными мотивами — что и как нужно поменять в метках времени, чтобы снизить возможности криминалистического исследования.
Временные метки файла в файловой системе и в метаданных
Прежде чем начать говорить о временных метках файла, нужно понять, что это является частью функциональности файловой системы. Следовательно, на различных файловых системах некоторые временные метки могут быть недоступны.
Кроме как в свойствах файла в файловой системе, временные метки могут храниться в метаданных самого файла. Очень многие форматы имеют метаданные и довольно часто эти метаданные содержат свой собственный набор временных меток, которые отличаются по составу и даже по текущему их значению от меток времени в файловой системе. Например, для документов Word в метаданных кроме даты создания могут также содержаться метки времени о дате печати файла, об общем времени редактировании файла и так далее.
Для изображений в EXIF метаданных также могут содержаться независимая от файловой системы информация о времени создания файла.
Нельзя ни в коем случае забывать о метаданных если ваша цель отредактировать метки времени! Для доступа/редактирования к метаданным используются различные приложения и эти вопросы не входят в тему данной статьи. Данная статья посвящена меткам времени файлов и папо, которые хранит файловая система.
Как посмотреть время создания, редактирования и открытия файла в Linux
Сразу все временные метки файла можно посмотреть с помощью команды stat. Запуск очень простой:
Например, я хочу узнать информацию о файле prog.txt:
С временем создания всё понятно — это когда файл был создан. Между прочим, эту информацию программа stat стала показывать довольно недавно. До этого на вопрос «как узнать время создания файла?» знатоки отвечали, что ядро Linux это не поддерживает. В качестве рабочего решения нужно было посмотреть иноду интересующего файла, а затем другой командой посмотреть дату создания этой иноды — это и считалось временем создания файла.
Выше показан вывод для команды на файловой системе ext4. Для файловой системы ext2 дата создания файла по-прежнему недоступна:
Теперь давайте разберёмся, что означают другие метки времени в Linux.
Что такое время доступа к файлу, время модификации файла и время изменения файла в Linux
Метки времени (timestamps):
Доступ (Access - last access) — время, когда файл был прочитан последний раз. Это время меняется при доступе таких системных вызовов как mknod(2), utimes(2) и read(2). Если это текстовый файл, то дата последнего доступа обновляется при каждом его открытии. Если это исполнимый файл, то дата доступа обновится при его запуске.
Но прежде чем придумывать практическое использование времени последнего доступа к файлу (например, чтобы проверить, какие команды запускалась за последний час), нужно учитывать очень важные нюансы. Выше показано, что поддержка определённых меток времени зависит от файловой системы. Ещё поведение меток времени зависит от того, с какими опциями была смонтирована файловая система. Например, при запуске скрипта мы ожидаем, что время доступа к этому файлу изменится на текущее. Это так — для выполнения скрипта его нужно прочитать и atime должна поменяться на время выполнения. Обычно это так и происходит. Но если запускаемый/читаемый файл находится в файловой системе, которая смонтирована с опциями noatime или relatime (или другими возможными опциями, которые могут повлиять на изменение atime — к пример «только чтение»), то поведение будет другим.
noatime
Не обновлять на этой файловой системе время доступа к иноде (например, для ускорения производительности за счёт исключения ненужных операций)
relatime
Обновлять время доступа к иноде относительно времени модификации или изменения. Время доступа обновляется только если предыдущее время доступа было более старым, чем время модификации или изменения, либо если предыдущее время доступа старше 1 дня.
Итак — время последнего доступа к файлу может быть неточным! По сути, если при монтировании диска используется опция relatime, то получается, что оно показывает:
1) время последнего изменения или модификации файла
2) время сегодняшнего первого открытия файла
3) ИНОГДА оно действительно показывает время последнего доступа к файлу (если сегодня файл открывали только один раз), либо если этот доступ к файлу выполнен после его модификации или изменения статуса
Модифицирован (Modify - last modified) — время последнего изменения содержимого файла. То есть если это текстовый файл, то время модификации поменяется когда вы его откроете и удалите какое-то слово или что-то допишите. Меняется системными вызовами mknod(2), utimes(2) и write(2).
Изменён (Change - last changed) — Время последнего изменения метаданных файлов в файловой системе. То есть если в файле изображения вы измените EXIF метаданные — это будет модификация (поскольку по сути поменяется содержимое файла). Примером Изменения файла является смена разрешений доступа к нему (чтение, запись, выполнение), смена владельца, группы и т. д. Меняется с chmod(2), chown(2), link(2), mknod
Что такое время доступа, время модификации и время изменения директории в Linux
Посмотреть метки времени папки можно также с помощью команды stat:
Например, чтобы посмотреть информацию о текущей папке:
Для папок время последнего доступа обновляется при просмотре списка файлов внутри неё. Действуют такие же правила, как и для файлов — зависит от опций, с которыми смонтирована файловая система.
При создании или удалении нового файла внутри директории, при модификации содержимого или изменении свойств файла внутри папки, одновременно обновляется и время изменения, и время модификации данной папки.
Какое время показывает команда ls -l
Если запустить команду ls с опцией -l, то она покажет последнее время модификации содержимого файла (или директории). Используя опцию --time можно вызвать показ других меток времени:
Как можно увидеть, с помощью ls невозможно вывести время создания файла, поскольку эта возможность была добавлена в ядро недавно.
Если добавить опцию --sort=time, то будет также выполнена сортировка по выбранной метки времени.
Имеется несколько сокращённых опций:
Можно выбрать формат времени:
Значением TIME_STYLE могут быть: full-iso, long-iso, iso, locale или +ФОРМАТ.
Значение ФОРМАТа как в date(1). Если значение ФОРМАТа равно ФОРМАТ1<новая строка>ФОРМАТ2, то ФОРМАТ1 применяется не к последним файлам, а ФОРМАТ2 к новым. Если TIME_STYLE начинается с «posix-», то он применяется только для локалей отличных от POSIX. Также, используемый стиль по умолчанию задаёт переменная окружения TIME_STYLE.
Есть сокращение популярного варианта:
Поиск и сортировка файлов по времени модификации, доступа и изменения
Теперь мы переходим к потрясающей команде find. С её помощью мы будем выводить нужные файлы по дате изменения, а также искать файлы, которые были изменены или к которым был получен определённое время назад.
Кстати, настоятельно рекомендуется к изучению следующий материал по команде find:
С помощью команды find можно настроить очень гибкий поиск и вывод результатов.
Предположим, я хочу просмотреть файлы в папке /etc отсортированные по дате модификации:
Аналогично, но файлы отсортированы по дате доступа:
И снова аналогичный пример, но файлы отсортированы по времени изменению статуса:
В команде find вы можете настроить формат даты под свои нужды, а также указать любые фильтры, которые она поддерживает. Например, ниже команда для показа файлов, отсортированных по дате модификации, но только тех из них, у которых в имени встречается ssh:
Или только файлов с расширением .desktop:
Или любых файлов, отсортированных по дате доступа, но только первых 20:
Поиск файлов по времени изменения
Для этого мы опять будем использовать команду find. Для поиска по времени у неё есть следующие опции:
-amin n
Последний раз к файлу был совершён доступ n минут назад.
-atime n
К файлу был совершён доступ n*24 часов назад. Когда find определяет, как много 24-часовых периодов назад к файлу был совершён доступ, любая дробная часть игнорируется, поэтому для соответствия -atime +1 к файлу должен быть сделан доступ по крайней мере два дня назад.
-cmin n
Статус файла был изменён последний раз n минут назад.
-ctime n
Последний раз статус файла был изменён n*24 часов назад. Смотрите комментарий для -atime для понимания, как округление влияет на интерпретацию времени изменения статуса файла.
-mmin n
Данные файла последний раз были изменены n минут назад.
-mtime n
Данные файла были изменены последний раз n*24 часов назад. Смотрите комментарий для -atime для понимания, как округление влияет на интерпретацию времени модификации файла.
То есть если в опции слово min — то число n трактуется как минуты, а если в опции присутствует слово time, то n трактуется как сутки.
В качестве числа n можно указать точное значение, а также выражения «больше чем n» и «меньше чем n» следующими способами:
С find можно строить весьма сложные конструкции условий чтобы найти именно то, что вам нужно. Далее всего несколько примеров — но количество возможностей безгранично. С опцией -name можно указывать шаблон имени, можно комбинировать с поиском по любым другим свойствам файла: размер, права доступа и т.д.
Чтобы найти все файлы, содержимое которых было изменено ровно 50 дней назад:
Чтобы найти все файлы, к которым был получен доступ ровно 50 дней назад:
Чтобы найти все файлы, содержимое которырх было модифицировано более 50 дней назад и менее 100 дней назад:
Чтобы найти файлы, статус которых был изменён за последний час:
Чтобы найти файлы, которые содержимое которых было модифицировано за последний час:
Чтобы найти все файлы, доступ к которым был сделан за последний час:
Чтобы найти все файлы в текущей папке с расширением .php, которые были изменены или созданы за последние 10 дней:
Чтобы найти все исполнимые файлы в текущей папке, которые были изменены или созданы за последние 10 дней:
Как отредактировать метки времени файла
С помощью команды touch можно изменить три метки времени файла или папки:
- время доступа
- время модификации
- время изменения статуса
Если вы хотите изменить все эти значения на текущее время, то достаточно запустить команду вида:
Можно отдельно поменять только время доступа или только время модификации, соответствующие опции:
С помощью опции -t можно установить любое время, на которое мы хотим поменять метки файла. Формат указания времени следующий:
В этой строке то, что в фигурных скобках, является необязательным. Значения букв следующее:
- CC – Первые две цифры года (от слова century — века)
- YY – Вторые две цифры года
- MM – Месяц года (01-12)
- DD – День месяца (01-31)
- hh – Часы дня (00-23)
- mm – Минуты часа (00-59)
- ss – Секунды (00-59)
Если не указать века или год вообще, то будет использоваться текущий год. Если не указать секунды, то значением по умолчанию является 00.
С помощью опции -t невозможно указать доли секунды и в выводе команды stat на их месте всегда будет 000000000
С помощью опции -d можно использовать разные более человечные способы указать время, например, «две недели назад». Это должно работать на английском, не знаю как с национальными языками. Опция -d (судя по описанию) понимает много разных вариантов синтаксиса, но для меня самым интересным свойством является возможность установить доли секунды, чтобы метки времени выглядели естественно, поскольку временные метки с девятью нулями на конце наводят мысль о том, что они спуфлены. Пример установки времени с указанием долей секунды:
То есть формат строки такой (из неё можно пропустить дефисы и двоеточия — опция -d всё равно её поймёт, но я их добавил для наглядности):
Как в Linux изменить время создания файла
Хотя файловая система ext4 поддерживает такое свойство файлов как «время создания», в ядро Linux возможность чтения этого свойства была добавлена недавно. Что касается возможности изменения времени создания файла, то такой функции нет (и не факт, что файловые системы это вообще поддерживают).
Но, как обычно, можно что-то придумать.
Время назад
При копировании файла в другой файл, его временем создания становится текущее время. Если перевести системное время на то, которое вы хотите установить для файла в качестве времени создания, а затем скопировать содержимое файла в новый, то таким образом можно установить любое время создания для нового файла.
Изменение времени создания в других ОС или файловых системах
В Интернете попадаются инструкции об изменении времени создания файла в операционных системах, которые это поддерживают. Также предлагается использовать некоторые другие файловые системы — например, сетевые, в которых также можно поменять это свойство.
Действительно, в Windows можно изменить не только время модификации файла, но и время создания. Но проблема в том, что при переносе в Linux сохраняется только время модификации. В качестве времени создания записывается текущее (то есть время создания иноды в файловой системе). И если это понимать — что временем создания файла является время модификации иноды, то становится понятно, невозможно никак перенести файл (ни в архиве, ни как-то ещё) с сохранением временем создания.
Также становится понятно, что из Windows невозможно изменить время создания файла, который находится в совместной папке в файловой системе ext4 (хотя я на всякий случай попробовал).
То есть вариантов поменять дату создания файла в Linux всего два: 1) поменять время системы; 2) низкоуровневое редактирование файловой системы (например, шестнадцатеричным редактором или специальными инструментами (если они существуют)) — с соответствующими рисками нарушить целостность файловой системы.
То есть плохая новость в том, что изменить время создания файла очень трудно или практически невозможно. Но хорошая новость в том, что время создания файла на практике почти не используется из-за того, что это поддерживают не все файловые системы, и в самой Linux эта поддержка появилась недавно. Например, команда find ничего не знает о времени создания файла (хотя может искать по другим трём меткам времени: модификация, доступ, изменение статуса), команда ls также не умеет отображать время создания файла (только обычные три метки времени).
Обычно используется время модификации файла, которое очень легко можно поменять с помощью touch.
Как изменить время создания и модификации файла в Windows
Кстати, если вам интересно, как поменять время создания файла в Windows, то это можно сделать с помощью утилиты NirCmd:
Я проверил команду nircmd.exe — сработала идеально в последней Windows 10.
Заключение
Путешествие в мир меток времени файлов в Linux оказалось намного длиннее чем я сам ожидал. Зато мы узнали о четырёх разных метках времени, о том, как их посмотреть и изменить, а также как искать файлы по любой из трёх меток времени.
Я об этом уже говорил, но напомню ещё раз — редактируя метки времени файла, не нужно забывать о соответствующих метках времени в метаданных самого файла (а не файловой системы). Про просмотр и удаление метаданных вы найдёте больше информации по ссылкам:
Несколько лет назад, когда я впервые познакомился с Android, я услышал от своего коллеги по работе, что Android предусматривает возможность установки модифицированных или самодельных прошивок. Признаться, тогда я был далек от этого. И даже пол года назад меня едва интересовали подобные вещи. Глубоко в душе, я был уверен: то, что делает производитель, уже предназначено для нормального использования.
Каково же было мое разочарование, когда я приобрел телефон из поднебесной, где заводскими настройками было запрещено использование Google, Skype, Facebook и других приложений. В принципе, на некоторые вещи можно было закрыть глаза, но когда мой телефон не подразумевал использование учетной записи Google — я взял с себя обещания обязательно разобраться во что бы мне это не стало.
Прошло пол года и мои кастомные прошивки с успехом используются по всему миру.
В данной серии статей пойдет речь о том, как делать reverse программирование для Android, реализовывать патчи, твики и моды.
Преамбула
И так! Давайте сперва определимся с понятиями, которые будут использоваться в данной статье. Ваше привычное понимание, при этом, может сильно отличаться.
Патч — изменение или замена существующего программного кода с целью модификации алгоритма программы.
Мод — как правило, добавление дополнительного функционала в существующий программный код без изменения алгоритма.
Твик — усовершенствование функционала программы с целью облегчения доступа к параметрам системы.
Также хочу заметить, что все примеры будет взяты для телефона HTC, но это не значит, что данная информация не может быть использована на других телефонах.
Обращаю ваше внимание, что я, как автор, не несу ответственности за возможную потерю данных на вашем телефоне в результате использования информации ниже.
Подготовка среды
Обойдусь без подробных инструкций как пользоваться тем или иным программным обеспечением. Если вам интересна данная статья и вы дочитали до этих строк, то я надеюсь, что вы уже опытный пользователь и имеете опыт использования, ну или хотя бы экспериментирования в данной области. Инструкций, статей и результатов тестирования полно в открытом доступе, впрочем, как и на Хабре. Также обойдусь без описания некоторых терминов, иначе статья получится очень большой и нудной. Будем писать только по делу. Уверен, что среда у вас уже давно стоит. Если нет, то предлагаю скачать и установить.
1. Android SDK. Это среда разработки приложений для Андроид. Для того чтобы делать модификации, нам обязательно придется проверять наш программный код. Среда разработки самое лучшее, что мы можем использовать.
2. Android Kitchen. Данная утилита позволит вам работать с образами системных партиций официальной ну или неофициальной прошивки.
3. JD-GUI. Декомпилятор программного кода языка Java. Сразу отмечу, что это самый лучший декомпилятор в плане удобства использования.
4. DJ Java Decompiler. Еще один декомпилятор, или дизассемблер, как некоторые любят называть, программного кода языка Java. Не удобен в плане использования, но разбирает код, который иногда не понимает JD-GUI.
5. smali. Еще один дизассемблер, но уже dalvik кода. smali нужен для дизассемблирования, а backsmali ассемблирования кода.
6. dex2jar. Утилита для конвертации исполняемых файлов Dalvik кода.
Преобразование прошивки
Разумеется, прошивка, которая стоит у вас на телефон от производителя оптимизирована с целью сокращения энергопотребления. Для того чтобы прошивку можно было изменять, ее необходимо преобразовать в формат, позволяющий изменять код. Для этого используется Android Kitchen. Можно конечно и руками, как я и делал раньше, пока не нашел эту самую «кухню». Как вытаскивать системную область из телефона, устанавливать среду, делать DEODEX прошивки, вы можете прочитать в интернете. Если вам уже ничего не понятно, я думаю стоит повременить со статьей, пока вы не наберете достаточно опыта.
После того, как прошивка из оптимизированного вида (ODEX — оптимизированный dalvik исполняемый код, если мне не изменяет память) стала DEODEX (то бишь НЕ оптимизированной), все исполняемые файлы готовы к модификации.
Непосредственно модификации
Создание патчей
Как я уже и рассказывал, мой телефон изначально имел запрет на использование Google. Ну хоть ты тресни, на Playstore не зайти, учетную запись не настроить, телефонная книга толком не синхронизируется. Зачем нужен такой Android? Долго копаясь в логах (logcat) самого устройства, я нашел записи, которые говорили о том, что использование Google запрещено. Самое не удобное в Android, вы видите лог, но не знаете какое системное приложение его производит. Чтобы отыскать от куда ноги растут, мне пришлось распотрошить все системные приложения до дизассемблированного Java кода. Это заняло достаточно времени, но я до сих пор пользуюсь проделанной работой при анализе и поиске нужного кода. Этапы получения подобного инструментария следующие:
1. Сделать DEODEX всей прошивки
2. Вашу новую DEODEX прошивку надо будет собрать и прошить на телефон. Как это делается — тема другой статьи.
3. Из каждого файла, находящегося в /system/framework вытащить файл classes.dex и переконвертировать в JAR посредством dex2jar.
4. Каждый полученный JAR открыть в JD-GUI и пере-сохранить в исходный код
5. Распаковать исходный код из архива.
В итоге у меня получилось столько папок, сколько было JAR файлов в /system/framework, и каждая папка имела структуру исходных кодов Java.
Путем несложных манипуляций, я быстро отыскал то место, которое генерировало записи в logcat.
Не будем рассматривать всю логику запрета, так как для каждого случая это отдельная история. Мне пришлось потратить пару часов, прежде чем я нашел где производятся проверки, построить в голове блок схему алгоритма и понять куда надо лезть, чтобы алгоритм чуть чуть «попортить».
Оказалось все просто. Есть подпрограмма, которая на основе заранее установленных констант, при обращении отвечала, относится ли телефон к Китаю или же нет.
Код находился в файле HTCExtension.jar, а класс, который содержал данную подпрограмму находился в \com\htc\util\contacts\BuildUtils$Customization.java
Распаковка и анализ оригинального файла
1. Сперва нам надо взять оригинальный DEODEX JAR файл, который отвечает за нужную нам часть кода. В нашем случае HTCExtension.jar.
2. Открыть любым архиватором и вытащить от туда classes.dex
3. С помощью конвертера dex2jar преобразовать его в JAR файл. Команда: dex2jar.bat classes.dex
4. Открыть полученный classes_dex2jar.jar файл в JD-GUI.
5. Да, чаще всего JD-GUI декомпилирует код не так как он выглядит в оригинале, оно и понятно, но читать вполне можно. В исходнике мы видим, что подпрограмма проверяет параметры проекта и языковой флаг прошивки. В нашем прискорбном случае возвращается значение TRUE.
6. Чтобы сделать патч, нам надо дизассемблировать сам Dalvik код. Для этого используем baksmali. Удобнее всего создать отдельную папку и положить туда три файла вместе: HTCExtension.jar, smali.jar и baksmali.jar. Даем команду java -Xmx512m -jar baksmali.jar -a -d -o HTCExtension -x HTCExtension.jar
- это API вашей версии Android. Для JB - это 16
- папка, где находятся все фреймворки прошивки.
В моем случае это была команда
java -Xmx512m -jar baksmali.jar -a 16 -d S:\dev\Android\Android-Kitchen\WORKING_JB_15\system\framework -o HTCExtension -x HTCExtension.jar
7. В нашей вновь созданной папке появилась папка HTCExtension, а в ней наши файлы с Dalvik кодом.
8. Отыскиваем файл по пути \com\htc\util\contacts\BuildUtils$Customization.java и смотрим код:
9. Страшно, не правда ли? Ничего же не понятно. Но, это дело поправимое. Создав несколько своих патчей и набив тем самым руку, вы легко сможете модифицировать код без сторонних средств. В нашем случае, в этом коде
происходит присваивание переменной v0 значение 1, то есть TRUE. Далее идут всякие проверки, и если телефон не китайский, то значение переменной изменяется:
10. Самый простой способ спасти отца русской демократии, это изменить код на следующий:
, то есть поменять значение переменной с 1 на 0. То есть что бы ни было, всегда бы возвращалось значение FALSE и в JD-GUI код выглядел бы как
11. Да, метод ,будет работать. Но мы же не ищем легких путей - это раз. Во-вторых не совсем красиво. Хочется кода что-то вроде
12. А как нам получить Dalvik код данного исходного кода? Для новичков мы сделаем небольшой трюк.
Создание Dalvik кода
1. Открываем Android SDK.
2. Создаем новый проект, и в наш единственный тестовый класс пишем следующий код
3. Компилируем наш проект и затем берем собранное приложение из рабочей области.
4. Кладем собранное приложение в папку, где мы с вами потрошили JAR файл.
5. Даем команду
8. Все, код для патчинга готов.
Накатывание патча
1. Dalvik код замусорен маркерами, указывающими строку кода в оригинальном исходном файле. Это нужно при выводе ошибок, если таковые имеются в вашей программе. Без указаний строк код также прекрасно работает.
2. Удаляем строки с нумерацией строк, копируем и заменяем метод (подпрограмму) в нашем \com\htc\util\contacts\BuildUtils$Customization.java файле.
3. Сохраняем файл. Да, забыл сказать, редактор нужен нормальный, например Notepad++ или EditPlus. Кому какой нравится.
Компиляция и сборка патченного JAR файла
1. С помощью backsmali мы распотрошили наш JAR файл, а теперь его надо собрать обратно.
2. Даем команду java -Xmx512m -jar smali.jar -a 16 HTCExtension -o classes.dex
3. В нашей папочке появляется файлик classes.dex
4. Снова открываем HTCExtension.jar файл архиватором и заменяем в нем существующий classes.dex на наш только что созданный.
5. Все, наш HTCExtension.jar содержит модифицированный программный код.
Замена оригинального файла на патченный
Обычно, для рядовых пользователей создаются специальные скрипты, которые через recovery заменяются. Но нам такое не интересно. Во-первых долго и нудно, во-вторых мы же опытные пользователи и можем себе позволить некоторые тонкости.
1. Заменить текущий рабочий файл можно следующими командами, если у вас уже стоит DEODEX прошивка и имеется root доступ:
2. Спасибо что дочитали до этого пункта, осталось немного.
3. После перезагрузки ваш новый патченный код вступит в силу.
4. Если код не работает или выскакивает ошибка, то путем не хитрых комбинаций можно вернуть назад резервную копию.
Эпилог
Да, кому-то показалось данная статья слишком специфичной, кому-то мало-понятной, а кому-то бесполезной. Я специально обошелся без углубленной детализации и иллюстраций как это все выглядит в живую и на практике. Во-первых, данный труд будет неблагодарный и только породить еще больше вопросов. Во-вторых, не хочу видеть армию пользователей на форумах, которые жалуются, что убили свой телефон.
К следующей статье я расскажу как делать Твики. Будет пример использования автоматической записи телефонных звонков родными средствами телефона. Спасибо за ваше внимание.
Читайте также: