diff --git a/source/ConsoleApplication.cc b/source/ConsoleApplication.cc index f746b94..c86dc6d 100644 --- a/source/ConsoleApplication.cc +++ b/source/ConsoleApplication.cc @@ -771,6 +771,128 @@ error: return result; } +int InjectDllAndStartHttpByPid(unsigned int pid, wchar_t* szDllPath, DWORD port) +{ + if(!EnableDebugPrivilege()){ + return 0; + } + int result = 0; + HANDLE hRemoteThread = NULL; + LPTHREAD_START_ROUTINE lpSysLibAddr = NULL; + HINSTANCE__* hKernelModule = NULL; + LPVOID lpRemoteDllBase = NULL; + HANDLE hProcess; + size_t ulDllLength; + wchar_t* dllName = (wchar_t*)L"wxhelper.dll"; + size_t dllNameLen = wcslen(dllName) * 2 + 2; + char* funcName = (char* )"http_start"; + size_t funcNameLen = strlen(funcName) + 1; + + HANDLE hStartHttp = NULL; + LPVOID portAddr = NULL; + HANDLE getProcThread = NULL; + + LPVOID paramsAddr = NULL; + LPVOID param1Addr = NULL; + LPVOID param2Addr = NULL; + LPVOID GetProcFuncAddr = NULL; + + DWORD params[2] = { 0 }; + + ulDllLength = (wcslen(szDllPath) + 1) * sizeof(wchar_t); + hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, pid); + if (!hProcess) { + goto error; + } + + lpRemoteDllBase = VirtualAllocEx(hProcess, NULL, ulDllLength, MEM_COMMIT, PAGE_READWRITE); + if (lpRemoteDllBase) + { + if (WriteProcessMemory(hProcess, lpRemoteDllBase, szDllPath, ulDllLength, NULL) + && (hKernelModule = GetModuleHandleW(L"kernel32.dll")) != 0 + && (lpSysLibAddr = (LPTHREAD_START_ROUTINE)GetProcAddress(hKernelModule, "LoadLibraryW")) != 0 + && (hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, lpSysLibAddr, lpRemoteDllBase, 0, NULL)) != 0) + { + WaitForSingleObject(hRemoteThread, INFINITE); + GetProcFuncAddr = FillAsmCode(hProcess); + param1Addr = VirtualAllocEx(hProcess, NULL, dllNameLen, MEM_COMMIT, PAGE_READWRITE); + if (param1Addr) { + SIZE_T dwWriteSize; + BOOL bRet = WriteProcessMemory(hProcess, (LPVOID)param1Addr, dllName, dllNameLen, &dwWriteSize); + if (!bRet) { + goto error; + } + } + param2Addr = VirtualAllocEx(hProcess, NULL, funcNameLen, MEM_COMMIT, PAGE_READWRITE); + if (param2Addr) { + SIZE_T dwWriteSize; + BOOL bRet = WriteProcessMemory(hProcess, (LPVOID)param2Addr, funcName, funcNameLen, &dwWriteSize); + if (!bRet) { + goto error; + } + } + + params[0] = (DWORD)param1Addr; + params[1] = (DWORD)param2Addr; + + paramsAddr = VirtualAllocEx(hProcess, NULL, sizeof(params), MEM_COMMIT, PAGE_READWRITE); + if (paramsAddr) { + SIZE_T dwWriteSize; + BOOL bRet = WriteProcessMemory(hProcess, (LPVOID)paramsAddr, ¶ms[0], sizeof(params), &dwWriteSize); + if (!bRet) { + goto error; + } + } + + DWORD dwRet = 0; + getProcThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)GetProcFuncAddr, paramsAddr, 0, NULL); + + if (getProcThread) + { + WaitForSingleObject(getProcThread, INFINITE); + GetExitCodeThread(getProcThread, &dwRet); + if (dwRet) { + hStartHttp = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)dwRet, (LPVOID)port, 0, NULL); + WaitForSingleObject(hStartHttp, INFINITE); + result = 1; + } + } + } + } +error: + if (hRemoteThread) { + CloseHandle(hRemoteThread); + } + if (getProcThread) { + CloseHandle(getProcThread); + } + if (hStartHttp) { + CloseHandle(hStartHttp); + } + + if (lpRemoteDllBase) { + VirtualFreeEx(hProcess, lpRemoteDllBase, ulDllLength, MEM_DECOMMIT | MEM_RELEASE); + } + if (param1Addr) { + VirtualFreeEx(hProcess, param1Addr, dllNameLen, MEM_DECOMMIT | MEM_RELEASE); + } + + if (param2Addr) { + VirtualFreeEx(hProcess, param1Addr, funcNameLen, MEM_DECOMMIT | MEM_RELEASE); + } + + if (paramsAddr) { + VirtualFreeEx(hProcess, param1Addr, sizeof(params), MEM_DECOMMIT | MEM_RELEASE); + } + + if (GetProcFuncAddr) { + VirtualFreeEx(hProcess, GetProcFuncAddr, sizeof(GetProcAddressAsmCode), MEM_DECOMMIT | MEM_RELEASE); + } + + CloseHandle(hProcess); + return result; +} + int InjectDll(wchar_t* szPName, wchar_t* szDllPath) { if(!EnableDebugPrivilege()){ @@ -825,6 +947,58 @@ int InjectDll(wchar_t* szPName, wchar_t* szDllPath) return result; } +int InjectDllByPid(unsigned int pid, wchar_t* szDllPath) +{ + if(!EnableDebugPrivilege()){ + return 0; + } + int result = 0; + HANDLE hRemoteThread; + LPTHREAD_START_ROUTINE lpSysLibAddr; + HINSTANCE__* hKernelModule; + LPVOID lpRemoteDllBase; + HANDLE hProcess; + size_t ulDllLength; + + ulDllLength = (wcslen(szDllPath) + 1) * sizeof(wchar_t); + hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, pid); + if (!hProcess) { + return 0; + } + + lpRemoteDllBase = VirtualAllocEx(hProcess, NULL, ulDllLength, MEM_COMMIT, PAGE_READWRITE); + if (lpRemoteDllBase) + { + if (WriteProcessMemory(hProcess, lpRemoteDllBase, szDllPath, ulDllLength, NULL) + && (hKernelModule = GetModuleHandleW(L"kernel32.dll")) != 0 + && (lpSysLibAddr = (LPTHREAD_START_ROUTINE)GetProcAddress(hKernelModule, "LoadLibraryW")) != 0 + && (hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, lpSysLibAddr, lpRemoteDllBase, 0, NULL)) != 0) + { + WaitForSingleObject(hRemoteThread, INFINITE); + VirtualFreeEx(hProcess, lpRemoteDllBase, ulDllLength, MEM_DECOMMIT | MEM_RELEASE); + CloseHandle(hRemoteThread); + CloseHandle(hProcess); + OutputDebugStringA("[DBG] dll inject success"); + printf("dll inject success"); + printf("dll path : %s ", szDllPath); + printf("pid : %d ", pid); + result = 1; + } + else + { + VirtualFreeEx(hProcess, lpRemoteDllBase, ulDllLength, MEM_DECOMMIT | MEM_RELEASE); + CloseHandle(hProcess); + result = 0; + } + } + else + { + CloseHandle(hProcess); + result = 0; + } + return result; +} + int UnInjectDll(wchar_t* szPName, wchar_t* szDName) { HMODULE hDll; @@ -878,8 +1052,9 @@ int main(int argc, char** argv) int port = 0; ULONG pid = 0; + unsigned int injectPid =0; - while ((param = getopt(argc, argv, "i:p:u:d:m:P:h")) != -1) + while ((param = getopt(argc, argv, "i:p:u:d:m:P:I:h")) != -1) { switch (param) { @@ -916,6 +1091,9 @@ int main(int argc, char** argv) case 'P': port = std::atoi(optarg); break; + case 'I': + injectPid = std::atoi(optarg); + break; default: abort(); break; @@ -925,6 +1103,24 @@ int main(int argc, char** argv) if (pid) { FindHandles(pid, (LPSTR)"_WeChat_App_Instance_Identity_Mutex_Name", TRUE, TRUE); } + if (injectPid != 0 && cDllPath[0] != 0) + { + if(cDllPath[0] != '\0') + { + if (port == 0) { + std::wstring wsPath = Utf8ToUnicode(cDllPath); + int ret = InjectDllByPid(injectPid, (wchar_t*)wsPath.c_str()); + printf(" 注入结果:%i \n", ret); + } + else + { + std::wstring wsPath = Utf8ToUnicode(cDllPath); + int ret = InjectDllAndStartHttpByPid(injectPid, (wchar_t*)wsPath.c_str(), port); + printf(" 注入结果:%i \n", ret); + } + } + } + if (cInjectprogram[0] != 0 && cDllPath[0] != 0) { diff --git a/src/config.cc b/src/config.cc index 2f86227..171ff88 100644 --- a/src/config.cc +++ b/src/config.cc @@ -1,6 +1,17 @@ -#include "config.h" +#include "pch.h" +#include "config.h" + namespace wxhelper { Config::Config(/* args */) {} Config::~Config() {} -} // namespace wxhelper \ No newline at end of file + + +void Config::Initialize(){ + port_ = GetPrivateProfileInt("config", "Port", 19088, "./config.ini"); +} +int Config::GetPort(){ + return port_; +} + + } // namespace wxhelper \ No newline at end of file diff --git a/src/config.h b/src/config.h index 5df279e..ffde254 100644 --- a/src/config.h +++ b/src/config.h @@ -1,15 +1,17 @@ -#ifndef WXHELPER_CONFIG_H_ +#ifndef WXHELPER_CONFIG_H_ #define WXHELPER_CONFIG_H_ -namespace wxhelper{ +namespace wxhelper { - class Config - { - private: - /* data */ - public: - Config(/* args */); - ~Config(); - }; -} +class Config { + public: + Config(/* args */); + ~Config(); + void Initialize(); + int GetPort(); + + private: + int port_; +}; +} // namespace wxhelper #endif \ No newline at end of file diff --git a/src/db.cc b/src/db.cc index 1737688..4c64ef7 100644 --- a/src/db.cc +++ b/src/db.cc @@ -5,6 +5,7 @@ #include "easylogging++.h" #include "wechat_function.h" +#include "utils.h" using namespace std; namespace wxhelper { @@ -117,8 +118,16 @@ int DB::Select(DWORD db_hanle, const char *sql, vector item; for (size_t i = 0; i < it.size(); i++) { if (!it[i].is_blob) { - string content(it[i].content); - item.push_back(content); + bool is_utf8 = Utils::IsTextUtf8(it[i].content, it[i].content_len); + if (is_utf8) { + string content(it[i].content); + item.push_back(content); + } else { + string base64_str = + base64_encode((BYTE *)it[i].content, it[i].content_len); + item.push_back(base64_str); + } + } else { string b64_str = base64_encode((BYTE *)it[i].content, it[i].content_len); diff --git a/src/global_context.cc b/src/global_context.cc index 909973d..c711671 100644 --- a/src/global_context.cc +++ b/src/global_context.cc @@ -11,14 +11,15 @@ void GlobalContext::initialize(HMODULE module) { module_ = module; DWORD base = Utils::GetWeChatWinBase(); config.emplace(); + config->Initialize(); log.emplace(); - log->initialize(); + log->Initialize(); hide_module.emplace(); #ifndef _DEBUG hide_module->Hide(module_); #endif - HttpServer::GetInstance().Init(19088); + HttpServer::GetInstance().Init(config->GetPort()); HttpServer::GetInstance().HttpStart(); DB::GetInstance().init(base); contact_mgr.emplace(ContactMgr{base}); diff --git a/src/http_handler.cc b/src/http_handler.cc index 1b94fdc..8450ca6 100644 --- a/src/http_handler.cc +++ b/src/http_handler.cc @@ -131,7 +131,6 @@ string Dispatch(struct mg_connection *c, struct mg_http_message *hm) { break; } case WECHAT_MSG_SEND_AT: { - break; wstring chat_room_id = GetWStringParam(j_param, "chatRoomId"); vector wxids = getArrayParam(j_param, "wxids"); wstring msg = GetWStringParam(j_param, "msg"); @@ -502,11 +501,11 @@ string Dispatch(struct mg_connection *c, struct mg_http_message *hm) { break; } case WECHAT_DO_OCR: { - // wstring image_path = GetWStringParam(j_param, "imagePath"); - // string text(""); - // int success = g_context.misc_mgr->DoOCRTask(WS2LPWS(image_path), text); - // json ret_data = {{"code", success}, {"result", "OK"}, {"text", text}}; - // ret = ret_data.dump(); + wstring image_path = GetWStringParam(j_param, "imagePath"); + string text(""); + int success = g_context.misc_mgr->DoOCRTask(WS2LPWS(image_path), text); + json ret_data = {{"code", success}, {"result", "OK"}, {"text", text}}; + ret = ret_data.dump(); break; } case WECHAT_SEND_PAT_MSG: { @@ -589,7 +588,7 @@ void HttpHandler::HandlerRequest(struct mg_connection *c, void *ev_data) { ret = res.dump(); } if (ret != "") { - mg_http_reply(c, 200, "", ret.c_str(), 0, 0); + mg_http_reply(c, 200, "Content-Type: application/json\r\n", "%s\n", ret.c_str()); } } else { mg_http_reply(c, 500, NULL, "%s", "Invalid URI"); diff --git a/src/log.cc b/src/log.cc index 77841a8..c955235 100644 --- a/src/log.cc +++ b/src/log.cc @@ -7,7 +7,7 @@ Log::Log(/* args */) {} Log::~Log() {} -void Log::initialize() { +void Log::Initialize() { el::Configurations conf; // 启用日志 diff --git a/src/log.h b/src/log.h index ebca51a..9718263 100644 --- a/src/log.h +++ b/src/log.h @@ -8,7 +8,7 @@ namespace wxhelper{ public: Log(/* args */); ~Log(); - void initialize(); + void Initialize(); }; diff --git a/src/misc_mgr.cc b/src/misc_mgr.cc index 3aa02b9..8462307 100644 --- a/src/misc_mgr.cc +++ b/src/misc_mgr.cc @@ -90,7 +90,7 @@ int MiscMgr::DoOCRTask(wchar_t *img_path, std::string &result) { for (unsigned int i = 0; i < num - 1; i++) { DWORD content = *(DWORD *)header; result += Utils::WstringToUTF8(READ_WSTRING(content, 0x14)); - + result += "\r\n"; header = content; } } diff --git a/src/send_message_mgr.cc b/src/send_message_mgr.cc index 7a9b38e..ec71383 100644 --- a/src/send_message_mgr.cc +++ b/src/send_message_mgr.cc @@ -5,6 +5,7 @@ #include "wechat_function.h" #include "db.h" +#include "contact_mgr.h" namespace wxhelper { SendMessageMgr::SendMessageMgr(DWORD base):BaseMgr(base) {} @@ -14,10 +15,9 @@ int SendMessageMgr::SendText(wchar_t* wxid, wchar_t* msg) { WeChatString to_user(wxid); WeChatString text_msg(msg); wchar_t** msg_pptr = &text_msg.ptr; - DWORD base = Utils::GetWeChatWinBase(); - DWORD send_message_mgr_addr = base + WX_SEND_MESSAGE_MGR_OFFSET; - DWORD send_text_msg_addr = base + WX_SEND_TEXT_OFFSET; - DWORD free_chat_msg_addr = base + WX_FREE_CHAT_MSG_OFFSET; + DWORD send_message_mgr_addr = base_addr_ + WX_SEND_MESSAGE_MGR_OFFSET; + DWORD send_text_msg_addr = base_addr_ + WX_SEND_TEXT_OFFSET; + DWORD free_chat_msg_addr = base_addr_ + WX_FREE_CHAT_MSG_OFFSET; char chat_msg[0x2D8] = {0}; __asm { PUSHAD @@ -44,6 +44,67 @@ int SendMessageMgr::SendText(wchar_t* wxid, wchar_t* msg) { int SendMessageMgr::SendAtText(wchar_t* chat_room_id, wchar_t** wxids, int len, wchar_t* msg) { int success = -1; + WeChatString * at_users = new WeChatString[len+1]; + std::wstring at_msg = L""; + int number =0; + for (int i = 0; i < len; i++) { + std::wstring nickname; + if (!lstrcmpiW((wchar_t *)wxids[i], (wchar_t *)L"notify@all")) { + nickname = L"所有人"; + } else { + ContactMgr contact{base_addr_}; + nickname = contact.GetContactOrChatRoomNickname(wxids[i]); + } + if (nickname.length() == 0) { + continue; + } + + WeChatString temp = {0}; + temp.ptr = (wchar_t *)wxids[i]; + temp.length = wcslen((wchar_t *)wxids[i]); + temp.max_length = wcslen((wchar_t *)wxids[i]) * 2; + memcpy(&at_users[number], &temp, sizeof(WeChatString)); + at_msg = at_msg + L"@" + nickname + L" "; + number++; + } + if (number < 1){ + return success; + } + std::wstring origin(msg); + at_msg += origin; + AtInner at_list = {0}; + at_list.start = (DWORD)at_users; + at_list.finsh = (DWORD)&at_users[number]; + at_list.end = (DWORD)&at_users[number]; + WeChatString to_user(chat_room_id); + WeChatString text_msg((wchar_t *)at_msg.c_str()); + wchar_t **msg_pptr = &text_msg.ptr; + + DWORD send_message_mgr_addr = base_addr_ + WX_SEND_MESSAGE_MGR_OFFSET; + DWORD send_text_msg_addr = base_addr_ + WX_SEND_TEXT_OFFSET; + DWORD free_chat_msg_addr = base_addr_ + WX_FREE_CHAT_MSG_OFFSET; + char chat_msg[0x2D8] = {0}; + __asm{ + PUSHAD + CALL send_message_mgr_addr + PUSH 0x0 + PUSH 0x0 + PUSH 0x0 + PUSH 0x1 + LEA EAX,at_list + PUSH EAX + MOV EAX,msg_pptr + PUSH EAX + LEA EDX,to_user + LEA ECX,chat_msg + CALL send_text_msg_addr + MOV success,EAX + ADD ESP,0x18 + LEA ECX,chat_msg + CALL free_chat_msg_addr + POPAD + } + LOG_IF((success == -1), ERROR) << "SendText fail"; return success; } int SendMessageMgr::SendImage(wchar_t* wxid, wchar_t* image_path) { @@ -51,11 +112,10 @@ int SendMessageMgr::SendImage(wchar_t* wxid, wchar_t* image_path) { WeChatString to_user(wxid); WeChatString path(image_path); char chat_msg[0x2D8] = {0}; - DWORD base = Utils::GetWeChatWinBase(); - DWORD send_message_mgr_addr = base + WX_SEND_MESSAGE_MGR_OFFSET; - DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET; - DWORD send_image_msg_addr = base + WX_SEND_IMAGE_OFFSET; - DWORD free_msg_addr = base + WX_FREE_CHAT_MSG_OFFSET; + DWORD send_message_mgr_addr = base_addr_ + WX_SEND_MESSAGE_MGR_OFFSET; + DWORD init_chat_msg_addr = base_addr_ + WX_INIT_CHAT_MSG_OFFSET; + DWORD send_image_msg_addr = base_addr_ + WX_SEND_IMAGE_OFFSET; + DWORD free_msg_addr = base_addr_ + WX_FREE_CHAT_MSG_OFFSET; DWORD temp = 0; WeChatString null_obj = {0}; __asm { @@ -88,11 +148,10 @@ int SendMessageMgr::SendFile(wchar_t* wxid, wchar_t* file_path) { WeChatString to_user(wxid); WeChatString path(file_path); char chat_msg[0x2D8] = {0}; - DWORD base = Utils::GetWeChatWinBase(); - DWORD app_msg_mgr_addr = base + WX_APP_MSG_MGR_OFFSET; - DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET; - DWORD send_file_addr = base + WX_SEND_FILE_OFFSET; - DWORD free_msg_addr = base + WX_FREE_CHAT_MSG_OFFSET; + DWORD app_msg_mgr_addr = base_addr_ + WX_APP_MSG_MGR_OFFSET; + DWORD init_chat_msg_addr = base_addr_ + WX_INIT_CHAT_MSG_OFFSET; + DWORD send_file_addr = base_addr_ + WX_SEND_FILE_OFFSET; + DWORD free_msg_addr = base_addr_ + WX_FREE_CHAT_MSG_OFFSET; DWORD temp = 0; WeChatString null_obj = {0}; __asm { @@ -150,9 +209,8 @@ int SendMessageMgr::ForwardMsg(wchar_t* wxid, unsigned long long msgid) { if (localid == 0) return 0; WeChatString to_user(wxid); - DWORD base = Utils::GetWeChatWinBase(); - DWORD forward_msg_addr = base + WX_FORWARD_MSG_OFFSET; - DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET; + DWORD forward_msg_addr = base_addr_ + WX_FORWARD_MSG_OFFSET; + DWORD init_chat_msg_addr = base_addr_ + WX_INIT_CHAT_MSG_OFFSET; __asm { PUSHAD PUSHFD diff --git a/src/utils.cc b/src/utils.cc index e2dc855..d92bd2b 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -1,7 +1,6 @@ -#include "pch.h" -#include "utils.h" - +#include "utils.h" +#include "pch.h" namespace wxhelper { std::wstring Utils::UTF8ToWstring(const std::string &str) { @@ -19,7 +18,7 @@ std::wstring Utils::AnsiToWstring(const std::string &input, DWORD locale) { MultiByteToWideChar(CP_UTF8, 0, input.c_str(), -1, &temp[0], wchar_len); return std::wstring(&temp[0]); } - + return std::wstring(); } @@ -102,8 +101,6 @@ void Utils::Hex2Bytes(const std::string &hex, BYTE *bytes) { } } - - bool Utils::IsDigit(std::string str) { if (str.length() == 0) { return false; @@ -119,7 +116,8 @@ bool Utils::IsDigit(std::string str) { bool Utils::FindOrCreateDirectoryW(const wchar_t *path) { WIN32_FIND_DATAW fd; HANDLE hFind = ::FindFirstFileW(path, &fd); - if (hFind != INVALID_HANDLE_VALUE && (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + if (hFind != INVALID_HANDLE_VALUE && + (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { FindClose(hFind); return true; } @@ -174,4 +172,43 @@ std::string Utils::WCharToUTF8(wchar_t *wstr) { return std::string(); } +bool Utils::IsTextUtf8(const char *str,int length) { + char endian = 1; + bool littlen_endian = (*(char *)&endian == 1); + + size_t i; + int bytes_num; + unsigned char chr; + + i = 0; + bytes_num = 0; + while (i < length) { + if (littlen_endian) { + chr = *(str + i); + } else { // Big Endian + chr = (*(str + i) << 8) | *(str + i + 1); + i++; + } + + if (bytes_num == 0) { + if ((chr & 0x80) != 0) { + while ((chr & 0x80) != 0) { + chr <<= 1; + bytes_num++; + } + if ((bytes_num < 2) || (bytes_num > 6)) { + return false; + } + bytes_num--; + } + } else { + if ((chr & 0xC0) != 0x80) { + return false; + } + bytes_num--; + } + i++; + } + return (bytes_num == 0); +} } // namespace wxhelper \ No newline at end of file diff --git a/src/utils.h b/src/utils.h index b5e50b1..284c324 100644 --- a/src/utils.h +++ b/src/utils.h @@ -47,6 +47,8 @@ class Utils { static std::string WCharToUTF8(wchar_t *wstr); + static bool IsTextUtf8(const char * str,int length) ; + template static std::vector split(T1 str, T2 letter) { std::vector arr; diff --git a/src/wechat_function.h b/src/wechat_function.h index 59ee100..a8719eb 100644 --- a/src/wechat_function.h +++ b/src/wechat_function.h @@ -133,9 +133,9 @@ //ocr -#define WX_INIT_OBJ_OFFSET 0x7a98f0 -#define WX_OCR_MANAGER_OFFSET 0x7ae470 -#define WX_DO_OCR_TASK_OFFSET 0x13230c0 +#define WX_INIT_OBJ_OFFSET 0x80a800 +#define WX_OCR_MANAGER_OFFSET 0x80f270 +#define WX_DO_OCR_TASK_OFFSET 0x13da3e0 //storage