OCRMe, dumping OneDrive Business OCR Data
#include <Windows.h>
#include <stdio.h>
#include <shlwapi.h>
#include "sqlite3.h"
#pragma comment(lib, "Shlwapi.lib")
#define SQLITE3 sqlite3
#define SQLITE3_STATEMENT sqlite3_stmt
#define DatabaseSqlite3Open2 sqlite3_open_v2
#define DatabaseSqlite3Close sqlite3_close
#define DatabaseSqlite3ReturnNull sqlite3_result_null
#define DatabaseSqlite3ReturnInt64 sqlite3_result_int64
#define DatabaseSqlite3GetCharString sqlite3_value_text
#define DatabaseSqlite3GetInt sqlite3_value_int
#define DatabaseSqlite3CreateFunction2 sqlite3_create_function_v2
#define DatabaseSqlite3PrepareQuery2 sqlite3_prepare_v2
#define DatabaseSqlite3Next sqlite3_step
#define DatabaseSqlite3GetColumnText sqlite3_column_text
#define DatabaseSqlite3GetColumnNameString sqlite3_column_name
#define DatabaseSqlite3CloseObject sqlite3_finalize
#define DatabaseSqlite3GetColumnCount sqlite3_column_count
#define FileReopenSecure freopen_s
#define StringToLongLong strtoll
#define StringPrintFormatA snprintf
#define CrtFileOpen fopen_s
#define CrtFileClose fclose
#define CrtFilePrintFormatted fprintf
#define PCBYTE const unsigned char *
SIZE_T WCharStringToCharString(_Inout_ PCHAR Destination, _In_ PWCHAR Source, _In_ SIZE_T MaximumAllowed)
{
INT Length = (INT)MaximumAllowed;
while (--Length >= 0)
{
#pragma warning( push )
#pragma warning( disable : 4244)
if (!(*Destination++ = *Source++))
return MaximumAllowed - Length - 1;
#pragma warning( pop )
}
return MaximumAllowed - Length;
}
INT StringCompareW(_In_ LPCWSTR String1, _In_ LPCWSTR String2)
{
for (; *String1 == *String2; String1++, String2++)
{
if (*String1 == '\0')
return 0;
}
return ((*(LPCWSTR)String1 < *(LPCWSTR)String2) ? -1 : +1);
}
INT StringCompareA(_In_ LPCSTR String1, _In_ LPCSTR String2)
{
for (; *String1 == *String2; String1++, String2++)
{
if (*String1 == '\0')
return 0;
}
return ((*(LPCSTR)String1 < *(LPCSTR)String2) ? -1 : +1);
}
PWCHAR StringCopyW(_Inout_ PWCHAR String1, _In_ LPCWSTR String2)
{
PWCHAR p = String1;
while ((*p++ = *String2++) != 0);
return String1;
}
BOOL MakeDebugConsole(VOID)
{
FILE* FilePointer = NULL;
BOOL bFlag = FALSE;
if (!AllocConsole())
{
MessageBoxA(NULL, "Fatal error: Unable to create output console", "Fatal Error", MB_OK);
return FALSE;
}
if (FileReopenSecure(&FilePointer, "CONIN$", "r", stdin) != ERROR_SUCCESS)
goto EXIT_ROUTINE;
if (FileReopenSecure(&FilePointer, "CONOUT$", "w", stdout) != ERROR_SUCCESS)
goto EXIT_ROUTINE;
if (FileReopenSecure(&FilePointer, "CONOUT$", "w", stderr) != ERROR_SUCCESS)
goto EXIT_ROUTINE;
bFlag = TRUE;
EXIT_ROUTINE:
if (!bFlag)
MessageBoxA(NULL, "Fatal error: Unable to set STDIO", "Fatal Error", MB_OK);
else
printf("[+] Debug output console successfully created\r\n");
return bFlag;
}
BOOL IsPathValidW(_In_ LPCWSTR FilePath)
{
HANDLE hFile = INVALID_HANDLE_VALUE;
hFile = CreateFileW(FilePath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return FALSE;
if (hFile)
CloseHandle(hFile);
return TRUE;
}
BOOL ValidateFilePathAndExtension(_In_ LPCWSTR FilePath)
{
BOOL bFlag = FALSE;
LPCWSTR Extension = NULL;
if (!IsPathValidW(FilePath))
{
printf("[-] Unable to open file. Make sure the file is copied or not in use.\r\n");
goto EXIT_ROUTINE;
}
Extension = PathFindExtensionW(FilePath);
if (StringCompareW(Extension, L".db") != ERROR_SUCCESS)
{
printf("[-] Unable to verify file. File requires .db extension\r\n");
goto EXIT_ROUTINE;
}
bFlag = TRUE;
EXIT_ROUTINE:
return bFlag;
}
VOID StringToLongLongCallbackRoutine(sqlite3_context* Context, INT ArgumentCounter, sqlite3_value** ArgumentVector)
{
LPCSTR String = (LPCSTR)DatabaseSqlite3GetCharString(ArgumentVector[0]);
INT Base = DatabaseSqlite3GetInt(ArgumentVector[1]);
PCHAR EndPoint = 0;
LONGLONG Result = 0;
BOOL bFlag = FALSE;
if (String == NULL)
goto EXIT_ROUTINE;
Result = StringToLongLong(String, &EndPoint, Base);
if (EndPoint == String)
goto EXIT_ROUTINE;
bFlag = TRUE;
EXIT_ROUTINE:
if (!bFlag)
DatabaseSqlite3ReturnNull(Context);
else
DatabaseSqlite3ReturnInt64(Context, Result);
}
BOOL DumpOneDriveBusinessOcrData(SQLITE3* DatabaseObject, LPCSTR TableName)
{
BOOL bFlag = FALSE;
CHAR Query[MAX_PATH] = { 0 };
SQLITE3_STATEMENT* StatementObject = NULL;
INT ReturnValue = 0, Index = 0;
FILE* FileObject = NULL;
StringPrintFormatA(Query, sizeof(Query), "SELECT * FROM \"%s\";", TableName);
printf("[+] Executing query: %s\r\n", Query);
ReturnValue = DatabaseSqlite3PrepareQuery2(DatabaseObject, Query, -1, &StatementObject, NULL);
if (ReturnValue != SQLITE_OK)
{
printf("[-] Query failed. Rows are not present in SQLite DB or there is a SQLite version mismatch\r\n");
goto EXIT_ROUTINE;
}
else
printf("[+] Query executed successfully\r\n");
Index = DatabaseSqlite3GetColumnCount(StatementObject);
if (Index == 0)
{
printf("[-] No data present in table. Skipping...\r\n");
goto EXIT_ROUTINE;
}
else
printf("[+] Query successfully returned data\r\n");
printf("[+] Searching for OCR column...\r\n");
for (INT i = 0; i < Index; i++)
{
LPCSTR ColumnString = DatabaseSqlite3GetColumnNameString(StatementObject, i);
if (ColumnString == NULL)
{
printf("[-] Empty column found... continuing\r\n");
continue;
}
if (StringCompareA(ColumnString, "MediaServiceOCR") == 0)
{
printf("[+] Column: %s found in %s\r\n", ColumnString, TableName);
bFlag = TRUE;
}
}
if (!bFlag)
{
printf("[-] MediaServiceOCR column not found.\r\n");
goto EXIT_ROUTINE;
}
else
bFlag = FALSE;
if (CrtFileOpen(&FileObject, "OcrOutput.csv", "w") != ERROR_SUCCESS)
{
printf("[-] Unable to create output CSV\r\n");
goto EXIT_ROUTINE;
}
if (FileObject == NULL)
{
printf("[-] Unable to create output CSV\r\n");
goto EXIT_ROUTINE;
}
else
printf("[+] Output CSV created in current directory\r\n");
printf("[+] Creating file headers\r\n");
for (INT i = 0; i < Index; i++)
{
LPCSTR ColumnString = DatabaseSqlite3GetColumnNameString(StatementObject, i);
if (ColumnString == NULL)
goto EXIT_ROUTINE;
CrtFilePrintFormatted(FileObject, "\"%s\"", DatabaseSqlite3GetColumnNameString(StatementObject, i));
if (i < Index - 1)
CrtFilePrintFormatted(FileObject, ",");
}
CrtFilePrintFormatted(FileObject, "\n");
printf("[+] Writing to CSV\r\n");
while ((ReturnValue = DatabaseSqlite3Next(StatementObject)) == SQLITE_ROW)
{
for (INT i = 0; i < Index; i++)
{
PCBYTE TextString = DatabaseSqlite3GetColumnText(StatementObject, i);
if (TextString == NULL)
CrtFilePrintFormatted(FileObject, "\"%s\"", "");
else
CrtFilePrintFormatted(FileObject, "\"%s\"", TextString);
if (Index < Index - 1)
CrtFilePrintFormatted(FileObject, ",");
}
CrtFilePrintFormatted(FileObject, ",");
}
if (ReturnValue != SQLITE_DONE)
{
printf("[-] Unable to walk all rows. Some data may be missing\r\n");
goto EXIT_ROUTINE;
}
else
printf("[+] Writing complete\r\n");
bFlag = TRUE;
EXIT_ROUTINE:
if (StatementObject)
DatabaseSqlite3CloseObject(StatementObject);
if (FileObject)
CrtFileClose(FileObject);
return bFlag;
}
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nShowCmd)
{
BOOL DebugConsole = FALSE;
BOOL bFlag = FALSE;
BOOL bIsWalked = FALSE;
LPWSTR* szArglist = NULL;
INT Arguments = 0;
WCHAR DbFilePath[MAX_PATH * sizeof(WCHAR)] = { 0 };
CHAR SqliteDbFilePath[MAX_PATH * sizeof(WCHAR)] = { 0 };
SQLITE3* DatabaseObject = NULL;
INT ReturnValue = 0;
LPCSTR Query = "SELECT name FROM sqlite_master WHERE name LIKE '%rows';";
SQLITE3_STATEMENT* StatementObject = NULL;
if (!MakeDebugConsole())
goto EXIT_ROUTINE;
else
DebugConsole = TRUE;
szArglist = CommandLineToArgvW(GetCommandLineW(), &Arguments);
if (szArglist == NULL || Arguments < 2)
{
printf("[-] No commandline argument or insufficient arguments.\r\n");
goto EXIT_ROUTINE;
}
if (StringCopyW(DbFilePath, szArglist[1]) == NULL)
{
printf("[-] Unable to copy argument buffer. Maybe the path is too long?\r\n");
goto EXIT_ROUTINE;
}
else
printf("[+] Target found: %ws\r\n", DbFilePath);
if (!ValidateFilePathAndExtension(DbFilePath))
goto EXIT_ROUTINE;
else
printf("[+] File successfully opened and validated\r\n");
if (WCharStringToCharString(SqliteDbFilePath, DbFilePath, (MAX_PATH * sizeof(WCHAR))) == 0)
{
printf("[-] Unable to transform WCHAR target to CHAR target for SQLite. Maybe the path is too long?\r\n");
goto EXIT_ROUTINE;
}
else
printf("[+] Target transformed from WCHAR to CHAR for SQLite\r\n");
ReturnValue = DatabaseSqlite3Open2(SqliteDbFilePath, &DatabaseObject, SQLITE_OPEN_READONLY | SQLITE_OPEN_URI, NULL);
if (ReturnValue != SQLITE_OK)
{
printf("[-] Unable to open SQLite database\r\n");
goto EXIT_ROUTINE;
}
else
printf("[+] Successfully opened SQLite database\r\n");
ReturnValue = DatabaseSqlite3CreateFunction2(DatabaseObject, "strtoll", 2, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, StringToLongLongCallbackRoutine, NULL, NULL, NULL);
if (ReturnValue != SQLITE_OK)
{
printf("[-] Unable to create StringToLongLong callback for target.\r\n");
goto EXIT_ROUTINE;
}
else
printf("[+] Successfully created StringToLongLong callback for target.\r\n");
printf("[+] Executing query: %s\r\n", Query);
ReturnValue = DatabaseSqlite3PrepareQuery2(DatabaseObject, Query, -1, &StatementObject, NULL);
if (ReturnValue != SQLITE_OK)
{
printf("[-] Query failed. Rows are not present in SQLite DB or there is a SQLite version mismatch\r\n");
goto EXIT_ROUTINE;
}
else
printf("[+] Query executed successfully\r\n");
printf("[+] Attemping to walk tables...\r\n");
while ((ReturnValue = DatabaseSqlite3Next(StatementObject)) == SQLITE_ROW)
{
PCBYTE Table = DatabaseSqlite3GetColumnText(StatementObject, 0);
if (Table == NULL)
{
printf("[-] Unable to walk tables\r\n");
goto EXIT_ROUTINE;
}
else
printf("[+] Walking table: %s\r\n", Table);
bIsWalked = TRUE;
if (DumpOneDriveBusinessOcrData(DatabaseObject, (const char*)Table))
break;
}
if (!bIsWalked)
printf("[-] Unabled to walk tables. OCR data may not be present.\r\n");
bFlag = TRUE;
EXIT_ROUTINE:
printf("[+] Press ENTER to terminate...\r\n");
#pragma warning( push )
#pragma warning( disable : 6031)
getchar();
#pragma warning( pop )
if (szArglist)
LocalFree(szArglist); //lol wtf microsoft
if (DatabaseObject)
DatabaseSqlite3Close(DatabaseObject);
return bFlag ? ERROR_SUCCESS : GetLastError();
}Last updated