前言
最近需要解析一个SQLCipher数据库文件,是带密码的那种,网上查询了好多资料,大多数都是说要在VS上查找“SQLCipher”数据库的NuGet包,然后直接就可以用了,但是我多次尝试,都失败了,总是提示“file is not a database”,也许我的文件比较特殊吧。
解决方案
搜资料时搜到了这个:SqlCipher: Sqlite 的加密版本SqlCipher加载 数据库
通过调用“sqlcipherbd.dll”动态库来解析SQLCipher数据库,该动态库还是C/C++开发的,支持C#、C、C++等语言的调用。
参考原始资料中代码写的比较乱,不方便看,我自己写了一个小demo实现了一下。
下面贴出来读取数据库的代码:
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog opendialog = new OpenFileDialog();
if (opendialog.ShowDialog() != DialogResult.OK)
{
return;
}
IntPtr _connection = IntPtr.Zero;
if (SqliteDLL.sqlite3_open(opendialog.FileName, out _connection) != SqliteDLL.SQLITE_OK)
return;
if (SqliteDLL.sqlite3_key(_connection, textBox1.Text, textBox1.Text.Length) != SqliteDLL.SQLITE_OK)
return;
IntPtr stmHandle;
string strsql = "select * from menu";
if (SqliteDLL.sqlite3_prepare_v2(_connection, strsql, strsql.Length, out stmHandle, IntPtr.Zero) != SqliteDLL.SQLITE_OK)
return;
int iColumnCount = SqliteDLL.sqlite3_column_count(stmHandle);
DataTable dataTableTmp = new DataTable();
for (int i = 0; i < iColumnCount; i++)
{
string columnName = Marshal.PtrToStringAnsi(SqliteDLL.sqlite3_column_name(stmHandle, i));
dataTableTmp.Columns.Add(columnName);
}
/* 获取每行数据 */
while (SqliteDLL.sqlite3_step(stmHandle) == SqliteDLL.SQLITE_ROW)
{
DataRow dataRowTmp = dataTableTmp.NewRow();
for (int i = 0; i < iColumnCount; i++)
{
byte[] byteArrayUnicode = Encoding.Unicode.GetBytes(Marshal.PtrToStringUni(SqliteDLL.sqlite3_column_text(stmHandle, i))); //转成UNICODE编码
dataRowTmp[i] = Encoding.UTF8.GetString(byteArrayUnicode);
}
dataTableTmp.Rows.Add(dataRowTmp);
}
SqliteDLL.sqlite3_finalize(stmHandle);
SqliteDLL.sqlite3_close(_connection);
dataGridView1.DataSource = dataTableTmp;
}
这里是导入“sqlcipherbd.dll”动态库 API的代码:
[DllImport(SQLCIPHERDLL, EntryPoint = "sqlite3_open", CallingConvention = CallingConvention.Cdecl)]
public static extern int sqlite3_open(string filename, out IntPtr db);
[DllImport(SQLCIPHERDLL, EntryPoint = "sqlite3_close", CallingConvention = CallingConvention.Cdecl)]
public static extern int sqlite3_close(IntPtr db);
[DllImport(SQLCIPHERDLL, EntryPoint = "sqlite3_key", CallingConvention = CallingConvention.Cdecl)]
public static extern int sqlite3_key(IntPtr db, string key, int keyLen);
[DllImport(SQLCIPHERDLL, EntryPoint = "sqlite3_prepare_v2", CallingConvention = CallingConvention.Cdecl)]
public static extern int sqlite3_prepare_v2(IntPtr db, string zSql, int nByte, out IntPtr ppStmpt, IntPtr pzTail);
[DllImport(SQLCIPHERDLL, EntryPoint = "sqlite3_step", CallingConvention = CallingConvention.Cdecl)]
public static extern int sqlite3_step(IntPtr stmHandle);
[DllImport(SQLCIPHERDLL, EntryPoint = "sqlite3_finalize", CallingConvention = CallingConvention.Cdecl)]
public static extern int sqlite3_finalize(IntPtr stmHandle);
[DllImport(SQLCIPHERDLL, EntryPoint = "sqlite3_errmsg", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr sqlite3_errmsg(IntPtr db);
[DllImport(SQLCIPHERDLL, EntryPoint = "sqlite3_column_count", CallingConvention = CallingConvention.Cdecl)]
public static extern int sqlite3_column_count(IntPtr stmHandle);
[DllImport(SQLCIPHERDLL, EntryPoint = "sqlite3_column_name", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr sqlite3_column_name(IntPtr stmHandle, int iCol);
[DllImport(SQLCIPHERDLL, EntryPoint = "sqlite3_column_type", CallingConvention = CallingConvention.Cdecl)]
public static extern int sqlite3_column_type(IntPtr stmHandle, int iCol);
[DllImport(SQLCIPHERDLL, EntryPoint = "sqlite3_column_int", CallingConvention = CallingConvention.Cdecl)]
public static extern int sqlite3_column_int(IntPtr stmHandle, int iCol);
[DllImport(SQLCIPHERDLL, EntryPoint = "sqlite3_column_text", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr sqlite3_column_text(IntPtr stmHandle, int iCol);
[DllImport(SQLCIPHERDLL, EntryPoint = "sqlite3_column_double", CallingConvention = CallingConvention.Cdecl)]
public static extern double sqlite3_column_double(IntPtr stmHandle, int iCol);
[DllImport(SQLCIPHERDLL, EntryPoint = "sqlite3_column_blob", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr sqlite3_column_blob(IntPtr stmHandle, int iCol);
[DllImport(SQLCIPHERDLL, EntryPoint = "sqlite3_column_bytes", CallingConvention = CallingConvention.Cdecl)]
public static extern int sqlite3_column_bytes(IntPtr stmHandle, int iCol);
[DllImport(SQLCIPHERDLL, EntryPoint = "sqlite3_bind_double", CallingConvention = CallingConvention.Cdecl)]
public static extern int sqlite3_bind_double(IntPtr stmHandle, int idx, double value);
[DllImport(SQLCIPHERDLL, EntryPoint = "sqlite3_bind_int", CallingConvention = CallingConvention.Cdecl)]
public static extern int sqlite3_bind_int(IntPtr stmHandle, int idx, int value);
[DllImport(SQLCIPHERDLL, EntryPoint = "sqlite3_bind_text", CallingConvention = CallingConvention.Cdecl)]
public static extern int sqlite3_bind_text(IntPtr stmHandle, int idx, byte[] value, int length, IntPtr callback);
[DllImport(SQLCIPHERDLL, EntryPoint = "sqlite3_bind_blob", CallingConvention = CallingConvention.Cdecl)]
public static extern int sqlite3_bind_blob(IntPtr stmHandle, int idx, byte[] value, int valueLen, IntPtr callback);
完整的项目代码放进Gitee了:ReadSQLCipher: C#通过“sqlcipherbd.dll”读取SQLCipher数据库,动态库“sqlcipherbd.dll”是C/C++开发的,导出了API,支持C#、C、C++等语言的调用。