Отслеживание запросов от приложения к файлу SQLite базы данных

в 17:54, , рубрики: sql, sqlite, метки:

К примеру, есть программа, из которой необходимо экспортировать какие-то данные. Это может быть, например каталог товаров конкурента, база вопросов для тестирования и т.д.

Обычно разработчики дают файлам с данными вполне осмысленные имена, и найти их несложно (расширение файла значения не имеет, важен внутренний формат). Здесь я рассмотрю вариант с базой данных на SQLite.

Выбор среди расширений для защиты SQLite баз данных небольшой. К тому же некоторые из них коммерческие. Компания Hwaci, Inc., отвечающая за разработку SQLite предлагает пару таких коммерческих расширений:

  • SQLite EncryptionExtension (SEE) шифрует БД по мере того как она пишется на диск, по-видимому эффективная штука.
  • Compressed and Encrypted Read-Only Database (CEROD) это расширение пошло дальше и не только шифрует БД, но еще и обеспечивает сжатие. Сжатие естественно уменьшает занимаемое базой данных место, но есть побочный эффект база данных становится read-only. Предлагают использовать при распространении лицензионных материалов.

Первый шаг определить зашифрована БД или нет. При моем первом знакомстве с SQLite я использовал программу SQLabs SQLiteManager. А эта программа иногда при открытии БД показывала сообщение «Database file seems to be encrypted. Please enter the encryption key». Казалось, что БД защищена каким-нибудь SEE или CEROD. Однако БД нормально открывалась стандартным sqlite3.exe или другой тулзой например SQLite Administrator.

Далее если схема БД нетривиальная, то разобраться что к чему довольно сложно. Или названия таблиц и полей могут быть сокращены до неузнаваемости. В таких случаях, хорошо бы посмотреть на SQL-запросы, которые программа делает к БД.

Способ, который далее описывается, работает, только если SQLite представлен в программе в виде внешней библиотеки (sqlite3.dll). Идея в том чтобы скомпилировать свой SQLite с логированием запросов и подменить им тот, который используется программой.

Я буду использовать компилятор Visual C++ 2010, но в принципе это неважно.

1) Открываем MSVC и создаем новый Win32 Project. В Application Wizard выбираем DLL и ставим галочку «Empty project».

2) Добавим в проект файлы sqlite3.h и sqlite3.c из исходников SQLite.

3) Также скопируем в папку с проектом файл sqlite3.def из дистрибутива SQLite. .def файл можно сгенерировать из файла sqlite3.dll с помощью утилиты dumpbin.

4) В свойствах проекта, добавим sqlite3.def в Linker -> Input -> Module definition file.

5) Также в С/С++ -> Preprocessor -> Preprocessor Definitions можно добавить:

  • WIN32
  • _DEBUG
  • _WINDOWS
  • _USRDLL
  • SQLITE3_EXPORTS
  • SQLITE_ENABLE_RTREE
  • SQLITE_ENABLE_COLUMN_METADATA
  • SQLITE_ENABLE_ATOMIC_WRITE
  • SQLITE_ENABLE_FTS3
  • SQLITE_ENABLE_FTS3_PARENTHESIS
  • SQLITE_ENABLE_FTS4
  • SQLITE_ENABLE_IOTRACE
  • SQLITE_ENABLE_LOCKING_STYLE
  • SQLITE_ENABLE_MEMORY_MANAGEMENT
  • SQLITE_ENABLE_MEMSYS3
  • SQLITE_ENABLE_MEMSYS5
  • SQLITE_ENABLE_STAT2
  • SQLITE_ENABLE_STAT3
  • SQLITE_ENABLE_UNLOCK_NOTIFY
  • SQLITE_ENABLE_UPDATE_DELETE_LIMIT

6) Изменим код в файле sqlite3.c, тут:

SQLITE_API int sqlite3_prepare_v2(
  sqlite3 *db,              /* Database handle. */
  const char *zSql,         /* UTF-8 encoded SQL statement. */
  int nBytes,               /* Length of zSql in bytes. */
  sqlite3_stmt **ppStmt,    /* OUT: A pointer to the prepared statement */
  const char **pzTail       /* OUT: End of parsed string */
){
  int rc;
  
  FILE *fp;
  fp = fopen("c:\sqlite3_prepare_v2.txt","a+");
  fprintf(fp, "%snn", zSql);
  fclose(fp); 
  
  rc = sqlite3LockAndPrepare(db,zSql,nBytes,1,0,ppStmt,pzTail);
  assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 );  /* VERIFY: F13021 */
  return rc;
}

и тут:

SQLITE_API int sqlite3_exec(
  sqlite3 *db,                /* The database on which the SQL executes */
  const char *zSql,           /* The SQL to be executed */
  sqlite3_callback xCallback, /* Invoke this callback routine */
  void *pArg,                 /* First argument to xCallback() */
  char **pzErrMsg             /* Write error messages here */
){
  int rc = SQLITE_OK;         /* Return code */
  const char *zLeftover;      /* Tail of unprocessed SQL */
  sqlite3_stmt *pStmt = 0;    /* The current SQL statement */
  char **azCols = 0;          /* Names of result columns */
  int nRetry = 0;             /* Number of retry attempts */
  int callbackIsInit;         /* True if callback data is initialized */
  
  FILE *fp;
  fp = fopen("c:\sqlite3_exec.txt","a+");
  fprintf(fp, "%snn", zSql);
  fclose(fp);

7) Теперь остается сделать билд (Ctrl + Shift + B) и подменить оригинальный sqlite3.dll используемый программой нашим новым sqlite3.dll. Далее, когда программа будет обращаться к БД через эти функции то все запросы будут попадать в лог.

После получения SQL запросов можно увидеть что-нибудь типа

SELECT customFunction(); 

что означает использование самописных расширений. Они могут быть вшиты в программу или быть «загружаемым» (loadable). Загружаемое расширение можно загрузить в свою программу с помощью функции:

int sqlite3_load_extension( sqlite3 *db, const char *ext_name, const char *entry_point, char **error )

Автор: Informatik

Поделиться

  1. Иван:

    Preprocessor Definitions нужно обязательно добавить ибо длл несоберется

* - обязательные к заполнению поля