/* (C) 2003-2007 Willem Jan Hengeveld * Web: http://www.xs4all.nl/~itsme/ * http://wiki.xda-developers.com/ * * $Id$ */ // todo: add code to // - generate a itsutils certificate // - sign itsutils.dll // DONE: upload certificate to the device // DONE: unlock rapi for device. // // this may also be needed in some cases: // // // // // // // // // // #include #include #include #include #include #include #include "vectorutils.h" #include "stringutils.h" #include "ptrutils.h" #include "FileFunctions.h" #include "debug.h" #include "dllversion.h" #include "checkdll.h" #include "itsutils.h" #include "appunlock.h" #include "streammultiplex.h" #include "util/boostthread.h" #ifndef _WIN32 // todo - does win32 have this file? #include #endif // configuration variables std::string g_dllname; // name of the local itsutils.dll std::string g_devicedllpath; // where to put itsutils.dll on the device. std::string g_devicelogpath; // where to put the itsutils.log file DWORD g_logtype= 0xFFFFFFFF; // non default log options // example config: // regutl -c HKCU\software\itsutils // regutl -s HKCU\software\itsutils ":devicedllpath=sz:\\storage card\\itsutils.dll" :logtype=dword:0 #ifdef _WIN32 BYTE *RapiAlloc(DWORD size) { return (BYTE*)LocalAlloc(LPTR, size); } void RapiFree(void *p) { LocalFree(p); } #else BYTE *RapiAlloc(DWORD size) { return (BYTE*)malloc(size); } void RapiFree(void *p) { free(p); } #endif std::string GetAppPath() { #ifdef WIN32 static TCHAR appname[1024]; if (!GetModuleFileName(NULL, appname, 1024)) return NULL; return ToString(appname); #else // todo return "./test123"; #endif } #ifdef _WIN32 BOOL ReadRegistryString(HKEY hRoot, const std::string& devicekey, const std::string& valname, std::string& strval) { HKEY hDevkey; LONG rc= RegOpenKeyEx(hRoot, ToTString(devicekey).c_str(), 0, KEY_READ, &hDevkey); if (rc) { //debug("rdstr-RegOpenKeyEx(%s): ERROR %08lx\n", devicekey.c_str(), rc); return FALSE; } DWORD valtype=0; DWORD maxsize=0; rc= RegQueryValueEx(hDevkey, ToTString(valname).c_str(), NULL, &valtype, NULL, &maxsize); if (rc==0 && maxsize==0) { strval.erase(); RegCloseKey(hDevkey); return TRUE; } if (rc!=0 && rc!=ERROR_MORE_DATA) { //debug("RegQueryValueEx('%s', '%s', string(%d)): ERROR %08lx\n", devicekey.c_str(), valname.c_str(), maxlen, rc); RegCloseKey(hDevkey); return FALSE; } ByteVector data; data.resize(maxsize+2); rc= RegQueryValueEx(hDevkey, ToTString(valname).c_str(), NULL, &valtype, &data[0], &maxsize); if (rc!=0) { //debug("RegQueryValueEx('%s', '%s', string(%d)): ERROR %08lx\n", devicekey.c_str(), valname.c_str(), maxlen, rc); RegCloseKey(hDevkey); return FALSE; } strval= ToString((TCHAR*)&data[0]); RegCloseKey(hDevkey); return TRUE; } BOOL ReadRegistryDword(HKEY hRoot, const std::string& devicekey, const std::string& valname, DWORD *pvalue) { HKEY hDevkey; LONG rc= RegOpenKeyEx(hRoot, ToTString(devicekey).c_str(), 0, KEY_READ, &hDevkey); if (rc) { //debug("rddw-RegOpenKeyEx(%s): ERROR %08lx\n", devicekey.c_str(), rc); return FALSE; } DWORD valtype=0; DWORD maxsize= sizeof(DWORD); rc= RegQueryValueEx(hDevkey, ToTString(valname).c_str(), NULL, &valtype, (LPBYTE)pvalue, &maxsize); if (rc) { //debug("RegQueryValueEx('%s', '%s', DWORD): ERROR %08lx\n", devicekey.c_str(), valname.c_str(), rc); RegCloseKey(hDevkey); return FALSE; } RegCloseKey(hDevkey); return TRUE; } #endif void read_itsutils_config() { #ifdef _WIN32 const std::string itsutilsregpath= "Software\\itsutils"; if (!ReadRegistryString(HKEY_CURRENT_USER, itsutilsregpath, "devicedllpath", g_devicedllpath)) g_devicedllpath= "\\windows\\itsutils.dll"; if (!ReadRegistryString(HKEY_CURRENT_USER, itsutilsregpath, "devicelogpath", g_devicelogpath)) g_devicelogpath= ""; if (!ReadRegistryDword(HKEY_CURRENT_USER, itsutilsregpath, "logtype", &g_logtype)) g_logtype= 0xFFFFFFFF; if (!ReadRegistryString(HKEY_CURRENT_USER, itsutilsregpath, "dllname", g_dllname)) g_dllname= "itsutils.dll"; #else g_devicedllpath= "\\windows\\itsutils.dll"; g_devicelogpath= ""; g_logtype= 0xFFFFFFFF; g_dllname= "itsutils.dll"; #endif } bool CeFileExists(const std::tstring& name) { DWORD dwAttr = CeGetFileAttributes( ToWString(name).c_str() ); if (0xFFFFFFFF == dwAttr) return false; return true; } // copied from pdel.cpp static bool DeleteCeFile(const std::string& filename) { CeSetFileAttributes(ToWString(filename).c_str(), 0); if (!CeDeleteFile(ToWString(filename).c_str())) { ceerror("DeleteFile %s", filename.c_str()); return false; } return true; } bool CeCopyFileToDevice(const std::string& srcfile, const std::string& dstfile, bool bOverwrite) { int fi= GetFileInfo(srcfile); if (fi==AT_NONEXISTANT) { error("Source/host file does not exist '%s'\n", srcfile.c_str()); return false; } else if (fi==AT_ISDIRECTORY) { error("Source/host file specifies a directory '%s'\n", srcfile.c_str()); return false; } std::string dstname= dstfile; size_t lastslash= srcfile.find_last_of("\\/"); std::string srcname= lastslash==srcfile.npos ? srcfile : srcfile.substr(lastslash+1); bool bExists= false; DWORD dwAttr = CeGetFileAttributes( ToWString(dstname).c_str()); if (0xFFFFFFFF != dwAttr) { if (dwAttr & FILE_ATTRIBUTE_DIRECTORY) { dstname += "\\"; dstname += srcname; dwAttr = CeGetFileAttributes( ToWString(dstname).c_str()); // File already exists. -> true // dstname is directory -> false ( meaning something like \windows\dllname.dll is a directory ) if ((dwAttr != 0xFFFFFFFF)) { if (dwAttr & FILE_ATTRIBUTE_DIRECTORY) { debug("ERROR: CeCopyFileToDevice to directory: %s\n", dstname.c_str()); return false; } else bExists= true; } else bExists= false; } else bExists= true; } if (!bOverwrite && bExists) { debug("NOTE: CeCopyFileToDevice not overwriting file %s\n", dstname.c_str()); return true; } MmapReader src(srcfile, MmapReader::readonly); HANDLE hDest = CeCreateFile( ToWString(dstname).c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hDest ) { ceerror("Unable to open WinCE file '%s'", dstname.c_str()); return false; } debug("Copying %s to WCE:%s\n", srcfile.c_str(), dstname.c_str()); ByteVector buffer; buffer.resize(32768); DWORD dwNumRead; do { dwNumRead= src.read(&buffer[0], buffer.size()); if (dwNumRead==0) break; DWORD dwNumWritten; if (!CeWriteFile( hDest, vectorptr(buffer), dwNumRead, &dwNumWritten, NULL)) { ceerror("Error !!! Writing WinCE file"); CeCloseHandle( hDest); return false; } } while (dwNumRead); CeCloseHandle( hDest); return true; } bool CeCopyDataToDevice(const ByteVector& data, const std::string& dstfile, bool bOverwrite) { bool bExists= false; DWORD dwAttr = CeGetFileAttributes( ToWString(dstfile).c_str()); if (0xFFFFFFFF != dwAttr) { if (dwAttr & FILE_ATTRIBUTE_DIRECTORY) { debug("ERROR: CeCopyDataToDevice to directory: %s\n", dstfile.c_str()); return false; } else bExists= true; } if (!bOverwrite && bExists) { debug("NOTE: CeCopyDataToDevice not overwriting file %s\n", dstfile.c_str()); return true; } HANDLE hDest = CeCreateFile( ToWString(dstfile).c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hDest ) { ceerror("Unable to open WinCE file '%s'", dstfile.c_str()); return false; } debug("Copying data to WCE:%s\n", dstfile.c_str()); DWORD dwOffset= 0; while (dwOffsetinvoke(method, insize, inbuf, poutsize, outbuf); } else { IRAPIStream *stream=NULL; HRESULT result= CeRapiInvoke(ToWString(g_devicedllpath).c_str(), ToWString(method).c_str(), insize, inbuf, poutsize, outbuf, &stream, 0); if (result==0 && stream) { g_stream= streammx_ptr(new streammx(stream)); //debug("created stream, s=%08lx, r=%08lx\n", g_stream, result); return (HRESULT)g_stream->getversion(); } return result; } } HRESULT ItsutilsGetVersion() { DWORD outsize=0; return CeRapiInvoke(ToWString(g_devicedllpath).c_str(), ToWString("ITGetVersion").c_str(), 0, NULL, &outsize, NULL, 0, 0); } void verify_size(void *begin, void *end, size_t size, const char *apiname) { if (size!=PTR_DIFF(begin, end)) { printf("WARNING: %s returned %d bytes, instead of %d\n", apiname, size, PTR_DIFF(begin, end)); } } bool need_to_reinit_rapi(DWORD res) { // check if WSA Error ( 0x80070000 + 10000..12000 ) return res==0x800775ab || (res>=0x80072710 && res<0x80072ee0); } #ifndef _WIN32 RapiConnection* g_synce_connection = NULL; #endif void CheckITSDll() { #ifndef _WIN32 if ((g_synce_connection = rapi_connection_from_name(0)) == NULL) { fprintf(stderr, "Could not find synce configuration\n"); exit(1); } rapi_connection_select(g_synce_connection); #endif if (FAILED(CeRapiInit())) { error("CeRapiInit failed\n"); exit(1); } std::string approot= GetAppPath(); approot.resize(approot.find_last_of("/\\")+1); // include trailing slash read_itsutils_config(); DWORD insize= 0; ByteVector indata; GetVersionParams *inbuf=NULL; if (g_logtype != 0xFFFFFFFF) { insize= sizeof(GetVersionParams)+g_devicelogpath.size(); indata.resize(insize); inbuf= reinterpret_cast(&indata[0]); inbuf->logtype= g_logtype; strcpy(inbuf->szLogfile, g_devicelogpath.c_str()); //debug("setting itsutils config(%08lx,%08lx): %d '%s'\n", insize, inbuf, g_logtype, g_devicelogpath.c_str()); } DWORD outsize=0; int appunlockstep=0; bool itsutilscopied=false; HRESULT res= 0; std::string srcdllname= is_absolute_path(g_dllname) ? g_dllname : approot+g_dllname; // todo: when copying fails due to a sharing violation, add sequence nr to itsutils.dll // so we can still operate, and possibly kill the offending thread / process // todo: change this to a loop with 1 time ItsutilsInvoke // if res==0x8007007e && fileexists -> fatal error: some dependency dll not found. while(1) { res= ItsutilsGetVersion(); if (res==ITSDLL_VERSION) { ItsutilsInvoke("ITStartStream", 0, NULL, &outsize, NULL); break; } else if (res==0x80070078) { // .. 80070078 : function not found if (!CeCopyFileToDevice(srcdllname, ToString(g_devicedllpath), true)) exit(1); itsutilscopied= true; continue; } else if (res==0x8007007e) { // .. 8007007e : (a) dll not found // todo-note: the thuraya sg2520 just returns 'E_FAIL' // failed to load module if (CeFileExists(ToTString(g_devicedllpath).c_str())) { debug("could not load itsutils.dll, an unknown module is missing\n"); debug("or maybe your device does not have an ARM processor\n"); exit(1); } if (!CeCopyFileToDevice(srcdllname, ToString(g_devicedllpath), true)) exit(1); itsutilscopied= true; continue; } else if (res>=0 && res<=0x10000000 && res!=ITSDLL_VERSION) { if (itsutilscopied) { debug("Could not update itsutils.dll to the current version, maybe it is inuse?\n"); debug("try restarting your device, or restart activesync\n"); debug("or maybe your device is application-locked.\n"); debug("or maybe the tool you are trying to use does not belong to the itsutils.dll\n"); exit(1); } // incorrect dll version if (!CeCopyFileToDevice(srcdllname, ToString(g_devicedllpath), true)) exit(1); itsutilscopied= true; continue; } else if (need_to_reinit_rapi(res)) { // activesync disconnected, try to reinitialize debug("rapi reinitializing\n"); if (FAILED(CeRapiInit())) { error("CeRapiInit failed\n"); exit(1); } } if (!appunlock(appunlockstep++)) { debug("ERROR loading itsutils.dll - probably denied by policy restriction\n"); exit(1); } } // E_FAIL -> itsutils.dll is missing, or execute denied by policy // res=0x8007007e : module not found // res=0x80070005 : EACCESSDENIED for policy restriction // res=0x800775ab : user answered 'no' to allow unsigned app to run? -> recall RapiInit // res=0x80072775 : rapi was deinitialized -> recall RapiInit // call ITGetVersion to enable debug logging res= ItsutilsInvoke("ITGetVersion", insize, (BYTE*)inbuf, &outsize, NULL); } void StopItsutils() { try { // problem: can't lock here, because the other thread may be waiting for an // itsutils answer ( and have locked ) //boost::mutex::scoped_lock lock(g_streammtx); g_stream.reset(); } catch(...) { printf("termination EXCEPTION\n"); } CeRapiUninit(); }