去教育版戳记讨论及源码(ARX,LISP版)

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;
}