去教育版戳记讨论及源码(ARX,LISP版)
- highflybird's Blog
- Log in or register to post comments
CAD教育版戳记有时候很讨厌,一旦图中有这个戳记,就会传染到其它图中。就是说从有戳记的图中拷贝到没戳记的,没戳记的也会带上。
而且这个戳记不太好消除。
对于2015-2018,Autodesk公司似乎已经把这个戳记移除掉了,也就是说,用这些版本的CAD打开图,看不到戳记,也不会弹出那个戳记警告框。
保存之后,图中不会再有了(这点我没有每个版本验证)。但该死的是到了2019后,这个又出现了,不知道autodesk公司在玩什么。
关于去教育版有很多方式,ARX的方式最有效。我这里提供用了ARX源码。以及附带的生成的ARX程序。
这个程序的源码 是从网上的 wdzhangsl 引申过来,特此深表感谢!
程序的原理是从数据库指针找到戳记的偏移地址,修改此处的值,便达到了消除。
因为原代码有些重复和繁琐,特此精简,并且升级到2019。经过测试,程序有效。
因为有些CAD版本我是没办法在自己机器安装,就没有一个个CAD版本进行测试其中的偏移地址。
如果有网友能测试到我程序中没有的CAD版本的值,请告诉我或者跟帖。多谢!
下面是程序的源码和已经编译好二进制文件的压缩包。欢迎各位捉虫子和提出建议。
程序的用法是:加载后,程序会自动消除戳记,也可以用手动输入命令QJYB 来消除。
如果你把此程序加载到启动组,以后每当打开有教育版的dwg后,程序会自动消除,你需要做的仅仅是保存文件而已。
对于LISp版本,更有这两项突出功能:
1、消除打开戳记文件的错误警告框。
2、全面兼容各种CAD版本。
LISP的代码如下:(需要DynamicWrapperX支持)
;|*************************************************************; 软件作者: Highflybird ; 软件用途: 消除CAD教育版戳记 ; 日期地点: 2019.05.12 深圳 ; 程序语言: AutoLISP,Visual LISP ; 版本号: Ver. 1.0.19.0512 ; ===============================================================; ================================================================ 本软件为开源软件: 以下是开源申明: ---------------------------------------------------------------- 本页面的软件遵照 GPL协议开放源代码,您可以自由传播和修改,在遵照 下面的约束条件的前提下: 一. 只要你在本开源软件的每一副本上明显和恰当地出版版权声明,保持 此许可证的声明和没有担保的声明完整无损,并和程序一起给每个其 他的程序接受者一份许可证的副本,你就可用任何媒体复制和发布你 收到的原始程序的源代码。你也可以为转让副本的实际行动收取一定 费用,但必须事先得到的同意。 二. 你可以修改本开源软件的一个或几个副本或程序的任何部分,以此形 成基于程序的作品。只要你同时满足下面的所有条件,你就可以按前 面第一款的要求复制和发布这一经过修改的程序或作品。 1.你必须在修改的文件中附有明确说明:你修改了这一文件及具体的修 改日期。 2.你必须使你发布或出版的作品(它包含程序的全部或一部分,或包含 由程序的全部或部分衍生的作品)允许第三方作为整体按许可证条款 免费使用。 3.如果修改的程序在运行时以交互方式读取命令,你必须使它在开始进 入常规的交互使用方式时打印或显示声明: 包括适当的版权声明和没 有担保的声明(或者你提供担保的声明);用户可以按此许可证条款 重新发布程序的说明;并告诉用户如何看到这一许可证的副本。(例 外的情况: 如果原始程序以交互方式工作,它并不打印这样的声明, 你的基于程序的作品也就不用打印声明。 三. 只要你遵循一、二条款规定,您就可以自由使用并传播本源代码,但 必须原封不动地保留原作者信息。 ================================================================ **************************************************************|; (defun c:QJYB (/ acdb addr DWX flag hMod isOK name pEMR pFlag hPROC Code CADVer OFFSET PID pNEW VMAP) (if (setq dwx (vlax-create-object "DynamicWrapperX")) (progn (DWX:Register DWX) (setq acdb (strcat "acdb" (substr (getvar "acadver") 1 2) ".dll")) (setq hMod (vlax-invoke DWX 'GetModuleHandle acdb)) (if (Is64Bit) (setq Name "?isEMR@AcDbDatabase@@QEBA_NXZ" pEMR (vlax-invoke DWX 'GetProcAddress hMod name) vMap '((172 . "180B") (180 . "800B") (181 . "880B") (182 . "900B") (190 . "A80B") (191 . "B00B") (230 . "A00B") (231 . "A00B") ) ) (setq Name "?isEMR@AcDbDatabase@@QBE_NXZ" pEMR (vlax-invoke DWX 'GetProcAddress hMod name) vMap '((150 . "A406") (160 . "D406") (161 . "D406") (162 . "D406") (170 . "DC07") (171 . "2408") (172 . "3408") (180 . "6C08") (181 . "7408") (182 . "7C08") (190 . "8408") (191 . "8408") (230 . "8408") ) ) ) (setq CADVer (fix (* 10 (atof (substr (getvar "acadver") 1 4))))) (setq offset (cdr (assoc CADVer vMap))) (setq Func (read name)) (if (zerop pEMR) (princ "\n你运行的CAD版本可能是2015-2018,它会自动消除教育版!") (if (and (/= 0 (setq pID (vlax-invoke DWX 'GetCurrentProcessId))) (/= 0 (setq hProc (vlax-invoke DWX 'OpenProcess 2035711 0 pID))) ;2035711 = PROCESS_ALL_ACCESS = 0x1F0FFF; ) (progn ;(FindOffset DWX acdb name) (if (Is64Bit) (setq Code (strcat "488B490833C08981" offset "00003C00C3")) (setq Code (strcat "8B490433C08981" offset "00003C00C3")) ) (setq pFlag (vlax-invoke DWX 'MemAlloc 4)) (setq isOK (vlax-invoke DWX 'VirtualProtect pEMR 30 4 pFlag)) ;PAGE_READWRITE = 0x4; (if (/= isOK 0) (setq pNew (vlax-invoke DWX 'MemWrite Code pEMR) flag (vlax-invoke DWX 'NumGet pFlag) isOK (vlax-invoke DWX 'VirtualProtect pEMR 30 flag pFlag) ) ) (vlax-invoke DWX 'MemFree pFlag) (vlax-invoke DWX 'CloseHandle hProc) (if (zerop isOK) (princ "\n程序运行失败!") (princ "\n程序运行成功!") ) ) ) ) ) (princ "\n你没有注册DynamicWrapperX!或者没有权限!") ) (princ) ) ;;;============================================================= ;;; 功能: 检查CAD是否为64位。 ;;; 输入: 无。 ;;; 输出: CAD如果是64位则返回T,否则返回nil。 ;;;============================================================= (defun Is64Bit () (= 27 (strlen (VL-PRINC-TO-STRING +))) ) ;;;============================================================= ;;; 功能: 注册一些与内存相关函数 ;;; 输入: DynamicWrapperX对象。 ;;; 输出: 无。 ;;;============================================================= (defun DWX:Register (DWX) (vlax-invoke DWX 'Register "KERNEL32" "LoadLibrary" "i=s" "r=p") (vlax-invoke DWX 'Register "KERNEL32" "FreeLibrary" "i=p" "r=l") (vlax-invoke DWX 'Register "KERNEL32" "GetCurrentProcessId" "r=l") (vlax-invoke DWX 'Register "KERNEL32" "OpenProcess" "i=lll" "r=p") (vlax-invoke DWX 'Register "KERNEL32" "VirtualProtect" "i=pllp" "r=l") (vlax-invoke DWX 'Register "KERNEL32" "VirtualProtectEx" "i=hpllp" "r=l") (vlax-invoke DWX 'Register "KERNEL32" "VirtualQueryEx" "i=hppl" "r=l") (vlax-invoke DWX 'Register "KERNEL32" "ReadProcessMemory" "i=hpplp" "r=l") (vlax-invoke DWX 'Register "KERNEL32" "WriteProcessMemory" "i=hpplp" "r=l") (vlax-invoke DWX 'Register "KERNEL32" "GetProcAddress" "i=hs" "r=p") (vlax-invoke DWX 'Register "KERNEL32" "GetModuleHandle" "i=s" "r=h") (vlax-invoke DWX 'Register "KERNEL32" "GetLastError" "r=l") (vlax-invoke DWX 'Register "KERNEL32" "CloseHandle" "i=h" "r=l") ) (vl-load-com) (princ "\n帮助说明: 程序运行命令是QJYB。注意: 请运行在打开有教育版戳记的图形前!!! ") (princ "\n命令运行一次后无需再输入,程序会自动消除教育版戳记了,你只需保存图即可。") (princ "\n如果你希望每次都能用,请加载到启动组。") (princ)
ARX的核心代码如下:
#include "stdafx.h" #include "GetProcess.h" #include "GetFunctionAddress.h" #include "RemoveEDU.h" byte OffsetAddress32[6][6]={ {0xa4,0x06,0xa4,0x06,0xa4,0x06},//R15 {0xd4,0x06,0xd4,0x06,0xd4,0x06},//R16 {0xdc,0x07,0x24,0x08,0x34,0x08},//R17 {0x6c,0x08,0x74,0x08,0x7c,0x08},//R18 {0x84,0x08,0x84,0x08,0x84,0x08},//R19 {0x84,0x08,0x84,0x08,0x84,0x08},//R23 }; byte OffsetAddress64[4][6]={ {0x18,0x0b,0x18,0x0b,0x18,0x0b},//R17 {0x80,0x0b,0x88,0x0b,0x90,0x0b},//R18 {0xa8,0x0b,0xb0,0x0b,0xb8,0x0b},//R19 {0xa0,0x0b,0xa0,0x0b,0xa0,0x0b},//R23 }; int RemoveEducationStamp(WNDINFO & wi) { if (wi.ActiveDoc==NULL) return FALSE; int ret = FALSE; CAcadUtility uti=wi.ActiveDoc.get_Utility(); //获取isEMR函数的RVA地址 INT_PTR offsetAddr = 0; LPCSTR funcName = wi.is64bit ? "?isEMR@AcDbDatabase@@QEBA_NXZ" : "?isEMR@AcDbDatabase@@QBE_NXZ"; HMODULE hDLL = LoadLibraryEx(wi.strAcDbPath,0,DONT_RESOLVE_DLL_REFERENCES);// LPVOID pIsEMR = GetProcAddress(hDLL,funcName); FreeLibrary(hDLL); if (pIsEMR == NULL) { pIsEMR = LoadFuncBase(wi.strAcDbPath,funcName,hDLL); } if (pIsEMR == NULL) { FUNCTION_ADDRESS funcSet; ret = getFuncAddr(wi.strAcDbPath,funcSet); FUNCTION_ADDRESS::iterator itr = funcSet.find(funcName); if (itr == funcSet.end()) { uti.Prompt(_T("找不到教育版相关函数!可能需要以管理员身份运行此程序,或者CAD版本是2015-2018.\n")); return FALSE; } offsetAddr = itr->second; pIsEMR = LPVOID((INT_PTR)wi.hAcDb + offsetAddr); //操纵数据库 } else { offsetAddr = (INT_PTR)pIsEMR - (INT_PTR)hDLL; pIsEMR = LPVOID((INT_PTR)wi.hAcDb + offsetAddr); //操纵数据库 } //以写的方式打开CAD进程 //HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, wi.dwProcessId); //权限要求太高,可能在某些机器上运行不了 HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION | PROCESS_VM_WRITE, FALSE, wi.dwProcessId); if (hProcess == NULL) { uti.Prompt(_T("无法打开CAD进程写入!\n")); return FALSE; } const DWORD bWritten = 128; byte pEAX = 0; SIZE_T dwNumberOfBytes = 0; CString strTemp = wi.strVersion.Mid(1,2); int nMajorVersion = _ttoi(strTemp.GetBuffer()); if (nMajorVersion>22) nMajorVersion-=3; strTemp = wi.strVersion.Mid(4,1); int nMinorVersion = _ttoi(strTemp.GetBuffer()); if (wi.is64bit) { byte AsmCode[15] = {0x48,0x8B,0x49,0x08,0x33,0xC0,0x89,0x81,0x00,0x00,0x00,0x00,0x3C,0x00,0xC3}; nMajorVersion = nMajorVersion-17; AsmCode[8]=OffsetAddress64[nMajorVersion][2*nMinorVersion]; AsmCode[9]=OffsetAddress64[nMajorVersion][2*nMinorVersion+1]; ret = WriteProcessMemory(hProcess,pIsEMR,AsmCode,sizeof(AsmCode),&dwNumberOfBytes); } else { byte AsmCode[14] = {0x8B,0x49,0x04,0x33,0xC0,0x89,0x81,0x00,0x00,0x00,0x00,0x3C,0x00,0xC3}; nMajorVersion = nMajorVersion-15; AsmCode[7]=OffsetAddress32[nMajorVersion][2*nMinorVersion]; AsmCode[8]=OffsetAddress32[nMajorVersion][2*nMinorVersion+1]; ret = WriteProcessMemory(hProcess,pIsEMR,AsmCode,sizeof(AsmCode),&dwNumberOfBytes); } if (!ret) { uti.Prompt(_T("修改内存模式失败!\n")); } else { wi.isRemoved = true; } CloseHandle(hProcess); return ret; }