维坦

感谢您的关注!


  • Jul
    23

    F#生成MD5字典

    Filed under: 编程; Tagged as: , ,

    之前的一篇文章提到过一种逆向思维求解MD5的方法.

    后来发现真的有人这么做了, 其中数据量最大的网站标称自己拥有4T的数据量, 可以解8位以下的数字和字母组合.

    即然是之前自己有过的想法, 那就拿F#来具体实现一下吧:

     
    #light
     
    namespace ViTarn
    module MD5
     
    open System
    open System.Data
    open System.Diagnostics
    open System.IO
    open System.Security.Cryptography
    open System.Text
     
    // sqlite.phxsoftware.com
    #r "System.Data.SQLite.DLL"
    open System.Data.SQLite
     
    // 数据库名
    let dbFile = "db.sqlite"
    // 连接字符串
    let connString =
        let csb = SQLiteConnectionStringBuilder ()
        csb.DataSource <- dbFile
        csb.ConnectionString
    // 秒表
    let watch = new Stopwatch ()
    watch.Start()
     
    // 为调试方便 输出对象并换行
    let debug x =
        x |> print_any
        Console.WriteLine ()
     
    // 从' '到'~'共95个常用于密码的字符 可以当成95进制看待
    let n2cl n =
        let rec prase n l =
            let d = n % 95
            let c = d + 32 |> char
            let d = n - d
            if d > 95 then
                prase (d / 95) (c :: l)
            else
                c :: l |> List.to_array
        prase n []
     
    // MD5加密字符串
    let md5 (str : string) =
        use md5Hasher = MD5.Create ()
        str
        |> Encoding.Default.GetBytes
        |> md5Hasher.ComputeHash
        |> Seq.map (fun x ->
            x.ToString "x2")
        |> Seq.fold (fun x y -> x + y) ""
     
    // 创建数据库
    if dbFile |> File.Exists |> not then
        dbFile |> SQLiteConnection.CreateFile
        use conn = new SQLiteConnection (connString)
        let sql = "PRAGMA auto_vacuum = 1; " +
                  "PRAGMA encoding = 'UTF-8'; " +
                  "PRAGMA page_size = 4096; " +
                  "PRAGMA synchronous = OFF; " +
                  "CREATE TABLE md5 (p VARCHAR(42)  NOT NULL  COLLATE NOCASE, s VARCHAR(9)  NOT NULL  COLLATE NOCASE);"
        use cmd = new SQLiteCommand (sql, conn)
        conn.Open ()
        cmd.ExecuteNonQuery () |> ignore
        conn.Close ()
     
    let main _ =
        let sql = "INSERT INTO md5 " +
                  "VALUES(?, ?); "
        use conn = new SQLiteConnection (connString)
        use cmd = new SQLiteCommand(sql, conn)
        cmd.Parameters.AddRange [|new SQLiteParameter(); new SQLiteParameter()|]
        conn.Open ()
        use tr = conn.BeginTransaction ()
        cmd.Transaction <- tr
        for i = 0 to 857374 do
            let s = new String (n2cl i)
            let p = md5 s
            cmd.Parameters.[0].Value <- p
            cmd.Parameters.[1].Value <- s
            cmd.ExecuteNonQuery () |> ignore
            if i % 10000 = 0 then
                i |> debug
        tr.Commit()
        conn.Close ()
     
    main ()
    watch.Elapsed |> debug
    Console.ReadKey()
     

    为什么是857374?
    95 * 95 * 95 - 1 = 857374

    模拟的95进制, 是从"空格"(' ')到波浪线('~')的, 用ASCII表示就是32到126.
    所在0 to 857374代表' '到'~~~'

    在我的电脑上耗时00:01:08
    CPU: AMD3000+
    RAM: 1G
    以DEBUG模式运行
    最终产生的数据库约36M+

    1 Comment
  • Jul
    8

    System.Data.SQLite DataType

    Filed under: 编程; Tagged as:

    虽然上篇文章中提到 SQLite 是弱类型数据库, 但对于 System.Data.SQLite 来说, 为了更好的与 Ado.NET 2.0 互操作, 作者仍然建议在创建表的时候就指定类型, 以方便程序进行类型转换, 下面是类型关键字列表:

    DbType.Boolean
        BIT
        YESNO
        LOGICAL
        BOOL

    DbType.Byte
        TINYINT

    DbType.Int16
        SMALLINT

    DbType.Int32
        INT

    DbType.Int64
        INTEGER
        BIGINT
        COUNTER
        AUTOINCREMENT
        IDENTITY
        LONG

    DbType.Single
        REAL

    DbType.Double
        DOUBLE
        FLOAT

    DbType.Decimal
        NUMERIC
        DECIMAL
        MONEY
        CURRENCY

    DbType.String
        CHAR
        NCHAR
        VARCHAR
        NVARCHAR
        TEXT
        NTEXT
        LONGTEXT
        LONGCHAR
        LONGVARCHAR
        STRING
        MEMO
        NOTE

    DbType.Binary
        BLOB
        BINARY
        VARBINARY
        IMAGE
        GENERAL
        OLEOBJECT

    DbType.Guid
        GUID
        UNIQUEIDENTIFIER

    DbType.DateTime
        TIME
        DATE
        DATETIME
        SMALLDATE
        SMALLDATETIME
        TIMESTAMP

    有两点需要注意:

    • SQLite 主键默认的 INTEGER 类型对应的是 Int64, 而 INT 对应的是 Int32.
    • 日期和时间类型最终存储为字符串, 所以格式必须符合ISO8601标准:
            "yyyy-MM-dd HH:mm:ss",
            "yyyyMMddHHmmss",
            "yyyyMMddTHHmmssfffffff",
            "yyyy-MM-dd",
            "yy-MM-dd",
            "yyyyMMdd",
            "HH:mm:ss",
            "THHmmss"
    No Comments
  • Jul
    7

    F# - open System.Data.SQLite

    Filed under: 编程; Tagged as: ,

    System.Data.SQLite.dll可以从http://sqlite.phxsoftware.com/得到

    冷门的语言+冷门的应用. 不多解释了, 直接上代码:

     
    #light
     
    open System
    open System.Data
    open System.IO
     
    #r "System.Data.SQLite.DLL"
    open System.Data.SQLite
     
    // 输出调试结果到命名行
    let debug x =
        x |> print_any
        Console.WriteLine()
     
    let space = [|for i in 1 .. 50 -> "*"|] |> string.Concat
     
    // 数据库文件名
    let dbFileName = "HelloSQLite.sqlite"
     
    // 连接字符串
    let connString =
        let builder = SQLiteConnectionStringBuilder()
        builder.DataSource <- dbFileName
        builder.ConnectionString
    space |> debug
    "连接字符串: " + connString |> debug
     
    // 创建数据库文件
    space |> debug
    "创建数据库..." |> debug
    try
        dbFileName |> File.Delete
        SQLiteConnection.CreateFile dbFileName
    with e ->
        e |> debug
     
    let test _ =
        use conn = new SQLiteConnection(connString)
        use cmd = conn.CreateCommand()
        conn.Open()
     
        // 开始计时
        let watch = new System.Diagnostics.Stopwatch()
        watch.Start()
     
        space |> debug
        "创建表..." |> debug
        try
            (* SQLite 官方文档提及的数据类型只有5种: NULL INTEGER NUMERIC TEXT BLOB
               你可能觉得太少了, 事实上 SQLite 是弱类型, 建表时定义的数据类型只是个标识, 对数据列的类型并没有实质性的作用.
               你也可以把 TEXT 类型定义成更复杂的 NVARCHAR(50) 之类的, 这只是为了未来数据迁移的方便. *)
            cmd.CommandText <- "CREATE TABLE [Test] ( " +
                               "[Int32] INTEGER NOT NULL, " +
                               "[String] TEXT NOT NULL)"
            cmd.ExecuteNonQuery() |> ignore
        with e ->
            conn.Close()
            e |> debug
     
        watch.Elapsed |> debug
        watch.Reset()
        watch.Start()
     
        space |> debug
        "乱入数据十万行..." |> debug
        use trans = conn.BeginTransaction()
        cmd.Transaction <- trans
        try
            // 注意: Int32这个列名有些误导, 封装的作者提及 INTEGER 对应 ADO.net 2.0 的 Int64 类型
            cmd.CommandText <- "INSERT INTO [Test](Int32, String) " +
                               "VALUES(?, ?)"
            // 下面这种生成参数的方式 在参数较多时会有方便 但不能用seq
            [for i in 0 .. 1 -> cmd.CreateParameter()]
            |> List.to_array
            |> cmd.Parameters.AddRange
            |> ignore
     
            // 参数化插入 这是官方推荐的方式 性能最好
            for i in 0 .. 99999 do
                let vl = [box i; box ((i * 1000).ToString())]
                for j = 0 to (cmd.Parameters.Count - 1) do
                    cmd.Parameters.[j].Value <- List.nth vl j
                cmd.ExecuteNonQuery() |> ignore
     
            trans.Commit()
        with e ->
            trans.Rollback()
            conn.Close()
            e |> debug
     
        watch.Elapsed |> debug
        watch.Reset()
        watch.Start()
     
        space |> debug
        "查询前3行..." |> debug
        try
            cmd.CommandText <- "SELECT * FROM [Test] " +
                               "WHERE [Int32]<3"
            use reader = cmd.ExecuteReader()
            while reader.Read() do
                reader.GetInt32 0 |> debug
                reader.GetString 1 |> debug
        with e ->
            e |> debug
     
        watch.Elapsed |> debug
        watch.Stop()
     
        conn.Close()
     
    test()
     
    Console.ReadKey()
     
    No Comments