]> xmof Git - DeDRM.git/commitdiff
Added source code for KRF utility
authorSatsuoni <satsuoni@gmail.com>
Wed, 21 May 2025 10:31:52 +0000 (19:31 +0900)
committerSatsuoni <satsuoni@gmail.com>
Wed, 21 May 2025 10:31:52 +0000 (19:31 +0900)
Other_Tools/KRFKeyExtractor/KRFKeyExtractor.cpp [new file with mode: 0644]
make_release.py

diff --git a/Other_Tools/KRFKeyExtractor/KRFKeyExtractor.cpp b/Other_Tools/KRFKeyExtractor/KRFKeyExtractor.cpp
new file mode 100644 (file)
index 0000000..6bd8b75
--- /dev/null
@@ -0,0 +1,777 @@
+///this file must be compiled for 32bit release
+#include <windows.h>
+#include <tchar.h>
+#include <stdio.h>
+#include <psapi.h>
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <vector>
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <functional>
+#include <set>
+#include <list>
+#include <strsafe.h>
+#pragma comment(lib, "User32.lib")
+
+
+/*
+* to find Cipher_Init, search for "assertion failed: ctx->cipher->block_size == 1 || ctx->cipher->block_size ==  8 || ctx->cipher->block_size == 16
+* and                            "assertion failed: EVP_CIPHER_CTX_iv_length(ctx) <= (int)sizeof(ctx->iv)"
+*
+* to find evp_EncryptDecryptUpdate, search for assertion failed: bl <= (int)sizeof(ctx->buf)
+* 
+* Another potential hook is rewriting the handlers of struct evp_cipher_st:
+* 
+* struct evp_cipher_st {
+    int nid;
+
+    int block_size;
+    int key_len;
+    int iv_len;
+
+    unsigned long flags;
+    int origin; //origin might not be present in Kindle version, it is older
+    int (*init) (EVP_CIPHER_CTX* ctx, const unsigned char* key,
+        const unsigned char* iv, int enc); //should be the replaced function, offset #5 in Kindle
+    int (*do_cipher) (EVP_CIPHER_CTX* ctx, unsigned char* out,
+        const unsigned char* in, size_t inl);
+* 
+* to do that, modifying the function that references structure in memory seems to work. Structures, so far, are hardcoded. Modifying structs directly triggers antitampering checks
+* ab 01 00 00 10 00 00 00 20 00 00 00 10 00 00 00 for aes256
+* a3 01 00 00 10 00 00 00 10 00 00 00 10 00 00 00 for aes128 
+* Handler function is shared for both
+*/
+
+
+//addresses extracted from Ghidra, md5 of dll is 22ccc712f186e3f03567e2bec2189d6a
+const int getPluginManagerAddress = 0x10029523;
+const int evp_Cipher_Init_thunk_address = 0x11637916;// 0x10015cb2;
+
+
+
+std::string hexStr(const uint8_t* data, int len)
+{
+    std::stringstream ss;
+    ss << std::hex;
+
+    for (int i(0); i < len; ++i)
+        ss << std::setw(2) << std::setfill('0') << (int)data[i];
+
+    return ss.str();
+}
+
+std::vector<char> HexToBytes(const std::string& hex) {
+    std::vector<char> bytes;
+
+    for (unsigned int i = 0; i < hex.length(); i += 2) {
+        std::string byteString = hex.substr(i, 2);
+        char byte = (char)strtol(byteString.c_str(), NULL, 16);
+        bytes.push_back(byte);
+    }
+
+    return bytes;
+}
+
+typedef void* (__thiscall* drmDataProv)(void*, const std::string& book, const std::list<std::string>& resources, const std::list<std::string>& vouchers);
+typedef void(__thiscall* openBook)(void* factory, void* bk, const std::string& name, const void* drmprovider, void* error, const std::list<std::string>& mayberes);
+typedef void* (__thiscall* KRFError)(void*);
+typedef void* (__cdecl* getBookFactory)();
+typedef void* (__thiscall* getPluginManager)();
+typedef void(__thiscall* loadAllStaticModules)(void*);
+
+struct krfErr
+{
+    int code;
+    std::string msg;
+    char padding[28];
+};
+
+int  setThunk(int thunkAddr, int funcaddr)
+{
+    int* offsetAddr = (int*)(thunkAddr + 1);
+    printf("offsetaddr %lx\n", *offsetAddr);
+    int oldThunk = *(offsetAddr);
+    oldThunk = oldThunk + (thunkAddr + 5);
+    int thunkOffset = funcaddr - (thunkAddr + 5);
+    DWORD vp = 0;
+    VirtualProtect(offsetAddr, 4, PAGE_EXECUTE_READWRITE, &vp);
+    *offsetAddr = thunkOffset;
+    VirtualProtect(offsetAddr, 4, vp, &vp);
+    return oldThunk;
+}
+
+struct KrfAccessFunctions
+{
+    getPluginManager GetPluginManager;
+    loadAllStaticModules LoadAllStaticModules;
+    drmDataProv DrmDataProvider;
+    getBookFactory GetBookFactory;
+    openBook OpenBook;
+
+    int base_offset;
+    int oldSSLCipherInit = 0;
+    int sslThunkAddr = 0;
+};
+
+KrfAccessFunctions globalKRFContext;
+
+typedef int(__cdecl* ssl_init)(void* ctx, void* ciper, void* engine, const unsigned char* key, const unsigned char* iv, int enc, uint8_t is_pipeline, const void* p);
+
+struct KeyData
+{
+    std::set<std::string> keys_128;
+    std::set<std::string> keys_256;
+    void reset()
+    {
+        keys_128.clear();
+        keys_256.clear();
+    }
+    void aggregate(KeyData* other)
+    {
+        if (other == nullptr) return;
+        keys_128.insert(other->keys_128.begin(), other->keys_128.end());
+        keys_256.insert(other->keys_256.begin(), other->keys_256.end());
+    }
+};
+KeyData keydataAccumulator;
+int __cdecl  fakessl_init(int* ctx, int* cipher, void* engine, const unsigned char* key, const unsigned char* iv, int enc, uint8_t is_pipeline, const void* p)
+{
+    printf("Calling openSSL hook\n");
+    int bytes = cipher[2];
+    std::string keystr = hexStr(key, bytes);
+    std::cout << "Key: (" << bytes*8 <<" bits) "<< keystr << std::endl;
+    if (bytes == 16)
+    {
+        keydataAccumulator.keys_128.insert(keystr);
+    }
+    if (bytes == 32)
+    {
+        keydataAccumulator.keys_256.insert(keystr);
+    }
+    std::cout << "Iv: " << hexStr(iv, 16) << std::endl;
+    ssl_init t_init = (ssl_init)globalKRFContext.oldSSLCipherInit;
+
+    int ret = t_init(ctx, cipher, engine, key, iv, enc, is_pipeline, p);
+    return ret;
+}
+
+
+void initKrfFunctions(HINSTANCE hinstLib, KrfAccessFunctions* out)
+{
+    const char* getpluginmanager = "?getPluginManager@PluginManager@Core@KRF@@SAAAV123@XZ";
+    const char* loadallstaticmodules = "?loadAllStaticModules@PluginManager@Core@KRF@@QAEXXZ";
+    const char* getbookfactory = "?getInstance@BookFactory@Core@KRF@@SAAAV123@XZ";
+    const char* openbook = "?openBook@BookFactory@Core@KRF@@QBE?AV?$shared_ptr@VBook@Core@KRF@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@5@ABVDRMDataProvider@23@AAVError@23@ABV?$list@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@5@@Z";
+    const char* drmdataprovider = "??0DRMDataProvider@Core@KRF@@QAE@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@ABV?$list@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@4@1@Z";
+    out->GetPluginManager = (getPluginManager)GetProcAddress(hinstLib, getpluginmanager);
+    out->LoadAllStaticModules = (loadAllStaticModules)GetProcAddress(hinstLib, loadallstaticmodules);
+    out->DrmDataProvider = (drmDataProv)GetProcAddress(hinstLib, drmdataprovider);
+    out->GetBookFactory = (getBookFactory)GetProcAddress(hinstLib, getbookfactory);
+    out->OpenBook = (openBook)GetProcAddress(hinstLib, openbook);
+    out->base_offset = (int)out->GetPluginManager - getPluginManagerAddress;
+    
+}
+void substituteThunk(KrfAccessFunctions* out)
+{
+    int thunkAddr = out->base_offset + evp_Cipher_Init_thunk_address;
+    out->oldSSLCipherInit = setThunk(thunkAddr, (int)&fakessl_init);
+    out->sslThunkAddr = thunkAddr;
+}
+struct DrmParameters
+{
+    std::string bookFile;
+    std::list<std::string> resources;
+    std::list<std::string> vouchers;
+};
+
+static bool ends_with(const std::string& str, const std::string& suffix)
+{
+    return str.size() >= suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
+}
+
+static bool starts_with(const std::string& str, const std::string& prefix)
+{
+    return str.size() >= prefix.size() && str.compare(0, prefix.size(), prefix) == 0;
+}
+
+// some extra helper overloads:
+
+static bool ends_with(const std::string& str, const char* suffix, unsigned suffixLen)
+{
+    return str.size() >= suffixLen && str.compare(str.size() - suffixLen, suffixLen, suffix, suffixLen) == 0;
+}
+
+static bool ends_with(const std::string& str, const char* suffix)
+{
+    return ends_with(str, suffix, std::string::traits_type::length(suffix));
+}
+
+static bool starts_with(const std::string& str, const char* prefix, unsigned prefixLen)
+{
+    return str.size() >= prefixLen && str.compare(0, prefixLen, prefix, prefixLen) == 0;
+}
+
+static bool starts_with(const std::string& str, const char* prefix)
+{
+    return starts_with(str, prefix, std::string::traits_type::length(prefix));
+}
+
+
+bool enumerateKindleFolder(TCHAR *path, DrmParameters * out)
+{
+    if (out == nullptr) return false;
+    WIN32_FIND_DATA ffd;
+    LARGE_INTEGER filesize;
+    TCHAR szDir[MAX_PATH];
+    size_t length_of_arg;
+    HANDLE hFind = INVALID_HANDLE_VALUE;
+    DWORD dwError = 0;
+    std::basic_string<TCHAR> conv = path;// std::basic_string<TCHAR>(path.begin(), path.end());
+    std::string shortPath = std::string(conv.begin(),conv.end());
+    StringCchCopy(szDir, MAX_PATH, path);
+    StringCchCat(szDir, MAX_PATH, TEXT("\\*"));
+    hFind = FindFirstFile(szDir, &ffd);
+    if (hFind == INVALID_HANDLE_VALUE)
+    {
+        return false;
+    }
+    do
+    {
+        std::basic_string<TCHAR> wfname = ffd.cFileName;
+        std::string fname=std::string(wfname.begin(), wfname.end());
+        std::string fullname= shortPath + "\\" + fname;
+        if (ends_with(fname, ".azw"))
+        {
+            out->bookFile = fullname;
+            std::cout << "Bookname " << fullname << std::endl;
+            continue;
+        }
+        if (ends_with(fname, ".voucher"))
+        {
+            out->vouchers.push_back(fullname);
+            continue;
+        }
+        if (ends_with(fname, ".res"))
+        {
+            out->resources.push_back(fullname);
+            std::cout << "Resource " << fullname << std::endl;
+            continue;
+        }
+      
+    } while (FindNextFile(hFind, &ffd) != 0);
+    FindClose(hFind);
+    if (out->bookFile.empty()) return false;
+    //if (out->vouchers.size() == 0) return false;
+    return true;
+    
+}
+
+
+
+int  tryOpeningBook(KrfAccessFunctions* ctx, const std::string& serial, const std::string& secret, DrmParameters* params,KeyData* out)
+{
+    keydataAccumulator.reset();
+    unsigned int sub[3000];
+    memset((void*)sub, 0, sizeof(sub));
+    std::list<std::string> secrets;
+    secrets.push_back(secret);
+    ctx->DrmDataProvider((void*)sub, serial, secrets, params->vouchers);
+    void* bookFactory = ctx->GetBookFactory();
+    int rebook[2];
+    krfErr err;
+    err.code = 0;
+    ctx->OpenBook(bookFactory, rebook, params->bookFile, sub, &err, params->resources);
+    std::cout << "BookOpen error " << err.code << " " << err.msg << std::endl;
+    if (err.code == 0)
+    {
+        out->aggregate( &keydataAccumulator);
+        //return true;
+    }
+    return err.code;
+}
+
+void initKRFContext(KrfAccessFunctions* ctx)
+{
+    void* pluginmanager = ctx->GetPluginManager();
+    ctx->LoadAllStaticModules(pluginmanager);
+}
+bool IsDotOrDotDot(const TCHAR* s)
+{
+    if (s[0] == TCHAR('.'))
+    {
+        if (s[1] == TCHAR('\0')) return true; // .
+        if (s[1] == TCHAR('.') && s[2] == TCHAR('\0')) return true; // ..
+    }
+    return false;
+}
+
+//stolen from StackOverflow
+template<class T>
+T base_name(T const& path, T const& delims = "/\\")
+{
+    return path.substr(path.find_last_of(delims) + 1);
+}
+template<class T>
+T remove_extension(T const& filename)
+{
+    typename T::size_type const p(filename.find_last_of('.'));
+    return p > 0 && p != T::npos ? filename.substr(0, p) : filename;
+}
+void enumerateKindleDir(const TCHAR* path,const std::string& outfile,std::set<std::string>* serial_candidates, std::set<std::string>* secret_candidates)
+{
+    WIN32_FIND_DATA ffd;
+    LARGE_INTEGER filesize;
+    TCHAR szDir[MAX_PATH];
+    TCHAR temp[MAX_PATH];
+    size_t length_of_arg;
+    HANDLE hFind = INVALID_HANDLE_VALUE;
+    DWORD dwError = 0;
+    StringCchCopy(szDir, MAX_PATH, path);
+    StringCchCat(szDir, MAX_PATH, TEXT("\\*"));
+    hFind = FindFirstFile(szDir, &ffd);
+    if (hFind == INVALID_HANDLE_VALUE)
+    {
+        std::cout << "Could not open directory" << std::endl;
+        return ;
+    }
+    std::ofstream out(outfile);
+    std::set<std::string> working_serials;
+    std::set<std::string> working_secrets;
+
+    do
+    {
+       
+        if (IsDotOrDotDot(ffd.cFileName)) continue;
+        if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+        {
+            _tprintf(TEXT("Trying to open  %s \n"), ffd.cFileName);
+            DrmParameters params;
+            StringCchCopy(temp, MAX_PATH, path);
+            StringCchCat(temp, MAX_PATH, TEXT("\\"));
+            StringCchCat(temp, MAX_PATH, ffd.cFileName);
+            if (enumerateKindleFolder(temp, &params))
+            {
+               // params.vouchers;
+                KeyData acc;
+                bool opened = false;
+                bool invalid = false;
+                // a silly optimization
+                for (auto& serial : working_serials)
+                {
+                    for (auto& secret : working_secrets)
+                    {
+                        int code = tryOpeningBook(&globalKRFContext, serial, secret, &params, &acc);
+
+                        if (code==0)
+                        {
+                            opened = true;
+                            std::cout << "Opened book with reused secret: " << secret << std::endl;
+                            break;
+                        }
+                        if (code == 14)
+                        {
+                            invalid = true;
+                            break;
+                        }
+                    }
+                    if (opened||invalid)break;
+                }
+                if (!opened&&!invalid)
+                {
+                    for (auto& serial : *serial_candidates)
+                    {
+                        for (auto& secret : *secret_candidates)
+                        {
+
+                            int code = tryOpeningBook(&globalKRFContext, serial, secret, &params, &acc);
+                            if (code == 0)
+                            {
+                                opened = true;
+                                working_serials.insert(serial);
+                                working_secrets.insert(secret);
+                                std::cout << "Opened book with secret: " << secret<<std::endl;
+                                break;
+                            }
+                            if (code == 14)
+                            {
+                                invalid = true;
+                                break;
+                            }
+                        }
+                        if (opened||invalid)break;
+                    }
+                }
+                if (invalid)
+                {
+                    std::cout << "Invalid book format, maybe MOBI?" << std::endl;
+                }
+                if (!opened&&!invalid)
+                {
+                    std::cout << "Could not open " << params.bookFile<< std::endl;
+
+                }
+                if (opened)
+                {
+                    if (acc.keys_128.size() + acc.keys_256.size() == 0)
+                    {
+                        std::cout << "Book opened, but no keys detected..." << std::endl;
+                    }
+                    else
+                    {
+                        for (auto& voucher : params.vouchers)
+                        {
+                            out << remove_extension(base_name(voucher));
+
+                            for (auto& key_128 : acc.keys_128)
+                            {
+                                out << "$" << "secret_key:" << key_128;
+                            }
+                            for (auto& key_256 : acc.keys_256)
+                            {
+                                out << "$" << "shared_key:" << key_256;
+                            }
+                            out << std::endl;
+                        }
+                    }
+                }
+            }
+           
+        }
+       
+    } while (FindNextFile(hFind, &ffd) != 0);
+    out.close();
+    FindClose(hFind);
+    //\"device_serial_number\":\"
+    std::cout << "Note: the below can be copied into text file and used instead of full memory dump. At least one of the secrets is likely to be spurious/incorrect. " << std::endl;
+    for (auto& serial : working_serials)
+    {
+        std::cout << "\"device_serial_number\":\"" << serial <<"\""<< std::endl;
+    }
+    for (auto& secret : working_secrets)
+    {
+        std::cout << "Working secret: \"" << secret << "\""<<std::endl;
+    }
+    return;
+}
+
+class basicSearchPattern
+{
+public:
+    virtual void ingest(char c) = 0;
+    virtual bool canStartPattern(char c) = 0;
+    virtual  std::set<std::string>* result() = 0;
+};
+class hexSearchPatternSingle : public basicSearchPattern
+{
+public:
+    struct Index
+    {
+        bool complete = false;
+        bool failed = false;
+        bool isReset = true;
+        std::vector<char> acc;
+        virtual void reset()
+        {
+            complete = false;
+            failed = false;
+            isReset = true;
+            acc.clear();
+        }
+    };
+    hexSearchPatternSingle(size_t len) :elen(len) { index.isReset = false; };
+    virtual bool canStartPattern(char c)
+    {
+        return isxdigit((int)(unsigned char)c);
+    }
+    virtual void ingestByIndex(Index* index, char c)
+    {
+        if (index->isReset || index->complete || index->failed)
+        {
+            return;
+        }
+        if (index->acc.size() == elen)
+        {
+            index->complete = true;
+            return;
+        }
+        if (isxdigit((int)(unsigned char)c))
+        {
+            index->acc.push_back(c);
+            if (index->acc.size() == elen)
+            {
+                index->complete = true;
+            }
+            return;
+        }
+        index->failed = true;
+    };
+    virtual void ingest(char c)
+    {
+        ingestByIndex(&index, c);
+        if (index.complete)
+        {
+            results.insert(std::string(index.acc.begin(), index.acc.end()));
+            index.reset();
+            index.isReset = false;
+            return;
+        }
+        if (index.failed)
+        {
+            index.reset();
+            index.isReset = false;
+            return;
+        }
+
+    };
+    virtual  std::set<std::string>* result()
+    {
+        return &results;
+    };
+private:
+    Index index;
+    size_t elen;
+    std::set<std::string> results;
+};
+
+class prePostPatternSingle :public  basicSearchPattern
+{
+public:
+    struct Index
+    {
+        size_t compIndex = 0;
+        bool inPrefix = true;
+        bool complete = false;
+        bool failed = false;
+        bool isReset = true;
+        std::vector<char> acc;
+        void reset()
+        {
+            compIndex = 0;
+            inPrefix = true;
+            complete = false;
+            failed = false;
+            isReset = true;
+            acc.clear();
+        }
+    };
+
+    prePostPatternSingle(const std::string& pre, const std::string& post, size_t maxlen, bool isAlphanum) :pre(pre), post(post), maxLen(maxlen), isAlphanum(isAlphanum)
+    {
+        if (!pre.empty())
+        {
+            fr = pre[0];
+        }
+        index.isReset = false;
+    };
+    virtual bool canStartPattern(char c)
+    {
+        if (pre.empty()) return true;
+        return c == fr;
+    }
+    void ingestByIndex(Index* index, char c)
+    {
+        if (index->isReset || index->complete || index->failed)
+        {
+            return;
+        }
+        if (index->inPrefix)
+        {
+            if (pre.empty())
+            {
+                index->inPrefix = false;
+            }
+            else
+            {
+                if (pre[index->compIndex] == c)
+                {
+                    index->compIndex++;
+                    if (index->compIndex >= pre.size())
+                    {
+                        index->inPrefix = false;
+                        index->compIndex = 0;
+                    }
+                    return;
+                }
+                else
+                {
+                    index->failed = true;
+                    return;
+                }
+            }
+        }
+        if (post.size() > 0)
+        {
+            if (index->compIndex > 0) //already in postfix
+            {
+                if (c != post[index->compIndex])
+                {
+                    index->failed = true;
+                    return;
+                }
+                index->compIndex++;
+                if (index->compIndex >= post.size())
+                {
+                    index->complete = true;
+                }
+                return;
+            }
+            else
+            {
+                if (c != post[index->compIndex])
+                {
+                    if (index->acc.size() >= maxLen)
+                    {
+                        index->failed = true;
+                        return;
+                    }
+                    if (isAlphanum && !isalnum((int)(unsigned char)c))
+                    {
+                        index->failed = true;
+                        return;
+                    }
+                    index->acc.push_back(c);
+                }
+                else
+                {
+                    index->compIndex++;
+                    if (index->compIndex >= post.size())
+                    {
+                        index->complete = true;
+                    }
+                    return;
+                }
+            }
+        }
+    }
+    virtual void ingest(char c)
+    {
+        ingestByIndex(&index, c);
+        if (index.complete)
+        {
+            results.insert(std::string(index.acc.begin(), index.acc.end()));
+            index.reset();
+            index.isReset = false;
+            return;
+        }
+        if (index.failed)
+        {
+            index.reset();
+            index.isReset = false;
+            return;
+        }
+
+    };
+
+    virtual  std::set<std::string>* result()
+    {
+        return &results;
+    };
+
+private:
+    std::string pre;
+    std::string post;
+    char fr = 0;
+    // std::vector<Index> indices;
+    Index index;
+    size_t maxLen;
+    bool isAlphanum = false;
+    std::set<std::string> results;
+};
+typedef basicSearchPattern* (__stdcall* patternFactory)();
+class multipatternSearch
+{
+public:
+    multipatternSearch() {};
+    size_t addLane(const std::shared_ptr<basicSearchPattern>& pattern)
+    {
+        lanes.push_back(pattern);
+        return lanes.size() - 1;
+    }
+    void ingest(char c)
+    {
+        for (auto& val : lanes)
+        {
+            val->ingest(c);
+        }
+    }
+    std::set<std::string>* results(size_t index)
+    {
+        return lanes[index]->result();
+    }
+
+private:
+
+    std::vector < std::shared_ptr<basicSearchPattern>> lanes;
+};
+
+int main(int argc, char* argv[])
+{
+    if (argc < 4)
+    {
+        std::cout << "Usage: executable <memdump path> <kindle documents path> <output file>" << std::endl;
+        std::cout << "This program needs accress to Kindle dlls, so it is easiest to run it in the folder with kindle executable or copied dlls" << std::endl;
+        std::cout << "Please ensure that KRFDynamic.dll is of the appropriate version (currently md5 22ccc712f186e3f03567e2bec2189d6a, kindle 2.7.1(70978)" << std::endl;
+        return 1;
+    }
+    HINSTANCE hinstLib;
+    hinstLib = LoadLibrary(TEXT("KRFDynamic.dll"));
+    std::string dump_path = argv[1];
+    std::string folder_path = argv[2];
+    std::string out_path = argv[3];
+    //this is a bit dumb but I don't know of good ways of flipping between char types
+    std::basic_string<TCHAR> wfolder_path = std::basic_string<wchar_t>(folder_path.begin(),folder_path.end());
+    if (hinstLib)
+    {
+        initKrfFunctions(hinstLib, &globalKRFContext);
+        initKRFContext(&globalKRFContext);
+        substituteThunk(&globalKRFContext); //has to go after init;
+        // Search for device serial and hex substrings
+        std::ifstream memdump(dump_path, std::ios::binary);
+        if (!memdump.is_open()) {
+            std::cerr << "Error opening dump file: " << dump_path << std::endl;
+            return 1;
+        }
+        std::cout << "Scanning dump file" << std::endl;
+        const size_t buffer_size = 1024 * 1024;
+        std::vector<char> buffer(buffer_size);
+        multipatternSearch search;
+        std::shared_ptr<prePostPatternSingle> dsn = std::make_shared<prePostPatternSingle>("\"device_serial_number\":\"", "\"", 60, true);
+        std::shared_ptr <hexSearchPatternSingle> hexpat = std::make_shared<hexSearchPatternSingle>(40);
+        search.addLane(dsn);
+        search.addLane(hexpat);
+        while (memdump.read(buffer.data(), buffer_size) || memdump.gcount() > 0) {
+            std::streamsize bytes_read = memdump.gcount();
+            for (std::streamsize i = 0; i < bytes_read; i++)
+            {
+                search.ingest(buffer[i]);
+            }
+        }
+        memdump.close();
+        std::set<std::string>* serial_candidates = search.results(0);
+        std::set<std::string>* secret_candidates = search.results(1);
+        if (serial_candidates->size() == 0)
+        {
+            std::cerr << "Could not find any serial candidates in dump" << std::endl;
+            return 1;
+        }
+        if (secret_candidates->size() == 0)
+        {
+            std::cerr << "Could not find any client secret candidates in dump" << std::endl;
+            return 1;
+        }
+
+        ///enumerate folder
+        printf(("Trying to open  %s \n"), argv[1]);
+        enumerateKindleDir(wfolder_path.data(),out_path, serial_candidates, secret_candidates);
+    }
+    else
+    {
+        std::cerr << "Could not load KRFDynamic.dll" << std::endl;
+        return 2;
+    }
+    
+    
+}
index 95bebf66b45b0c36a7d26b8f1e2d211f6fa9bc06..2f4a3667ceb2a647bb7a97349687564690237a25 100755 (executable)
@@ -18,6 +18,7 @@ DEDRM_SRC_TMP_DIR = 'DeDRM_plugin_temp'
 DEDRM_README= 'DeDRM_plugin_ReadMe.txt'
 OBOK_SRC_DIR = 'Obok_plugin'
 OBOK_README = 'obok_plugin_ReadMe.txt'
+KRFTool="Other_Tools/KRFKeyExtractor/KRFKeyExtractor.exe"
 RELEASE_DIR = 'release'
 
 def patch_file(filepath):
@@ -78,6 +79,10 @@ def make_release(version):
     shutil.move(OBOK_SRC_DIR+'.zip', RELEASE_DIR)
     shutil.copy(DEDRM_README, RELEASE_DIR)
     shutil.copy(OBOK_README, RELEASE_DIR)
+    try:
+        shutil.copy(KRFTool, RELEASE_DIR)
+    except:
+        pass
     shutil.copy("ReadMe_Overview.txt", RELEASE_DIR)
 
     # Remove temp folder: