云注入去签功能解析

云注入去签功能解析

之前用了一个app去去签,想改一些东西,顺便在去签的时候就把注入的dex的东西一起去解析看了,看看到达是怎么解析的,顺便之后把里面的抓包检测的功能也一并给解析了,增加一下代码功底顺便了解一下云注入的去签和抓包检测的原理

这里多出来了classes2.dex,拿jadx来反编译看看
s
\image-20241203161112978.png)

public class KillerApplication extends App {public static final String URL = "https://github.com/L-JINBIN/ApkSignatureKillerEx";static {killPM("bbs.shell", "MIIEqDCCA5CgAwIBAgIJAJNurL4H8gHfMA0GCSqGSIb3DQEBBQUAMIGUMQswCQYDVQQGEwJVUzET\nMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5k\ncm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYT\nYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODAyMjkwMTMzNDZaFw0zNTA3MTcwMTMzNDZaMIGUMQsw\nCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQ\nMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG\nCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCC\nAQgCggEBANaTGQTexgskse3HYuDZ2CU+Ps1s6x3i/waMqOi8qM1r03hupwqnbOYOuw+ZNVn/2T53\nqUPn6D1LZLjk/qLT5lbx4meoG7+yMLV4wgRDvkxyGLhG9SEVhvA4oU6Jwr44f46+z4/Kw9oe4zDJ\n6pPQp8PcSvNQIg1QCAcy4ICXF+5qBTNZ5qaU7Cyz8oSgpGbIepTYOzEJOmc3Li9kEsBubULxWBjf\n/gOBzAzURNps3cO4JFgZSAGzJWQTT7/emMkod0jb9WdqVA2BVMi7yge54kdVMxHEa5r3b97szI5p\n58ii0I54JiCUP5lyfTwE/nKZHZnfm644oLIXf6MdW2r+6R8CAQOjgfwwgfkwHQYDVR0OBBYEFEhZ\nAFY9JyxGrhGGBaR0GawJyowRMIHJBgNVHSMEgcEwgb6AFEhZAFY9JyxGrhGGBaR0GawJyowRoYGa\npIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRh\naW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5k\ncm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJAJNurL4H8gHfMAwGA1Ud\nEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHqvlozrUMRBBVEY0NqrrwFbinZaJ6cVosK0TyIU\nFf/azgMJWr+kLfcHCHJsIGnlw27drgQAvilFLAhLwn62oX6snb4YLCBOsVMR9FXYJLZW2+TcIkCR\nLXWG/oiVHQGo/rWuWkJgU134NDEFJCJGjDbiLCpe+ZTWHdcwauTJ9pUbo8EvHRkU3cYfGmLaLfgn\n9gP+pWA7LFQNvXwBnDa6sppCccEX31I828XzgXpJ4O+mDL1/dBd+ek8ZPUP0IgdyZm5MTYPhvVqG\nCHzzTy3sIeJFymwrsBbmg2OAUNLEMO6nwmocSdN2ClirfxqCzJOLSDE4QyS9BAH6EhY6UFcOaE0=\n");killOpen("bbs.shell");}
}

主要执行的去签就是换签名。这里调用的两个函数,一个换签,一个是apk更新的

先从killOpen来看看

private static void killOpen(String str) {try {System.loadLibrary("SignatureKiller");String apkPath = getApkPath(str); // /data/app/bbs.shell-Zxlv5blPzqvPR7OphdRWfg==/base.apkif (apkPath == null) {System.err.println("Get apk path failed");return;}File file = new File(apkPath);File file2 = new File(getDataFile(str), "origin.apk"); // /data/user/0/bbs.shelltry { // 下面全是检查了,检查的是这个origin.apk的更新,要是和这里的/data/user/0/bbs.shell匹配不上就把/data/app/bbs.shell-Zxlv5blPzqvPR7OphdRWfg==/base.apk这里的apk里面的origin.apk搞过去ZipFile zipFile = new ZipFile(file); // 下面全是检查了,检查的是这个origin.apk的更新,要是和这里的/data/user/0/bbs.shell匹配不上就把/data/app/bbs.shell-Zxlv5blPzqvPR7OphdRWfg==/base.apk这里的apk里面的origin.apk搞过去ZipEntry entry = zipFile.getEntry("assets/SignatureKiller/origin.apk");if (entry == null) {PrintStream printStream = System.err;printStream.println("Entry not found: assets/SignatureKiller/origin.apk");zipFile.close();return;}if (!file2.exists() || file2.length() != entry.getSize()) {InputStream inputStream = zipFile.getInputStream(entry);FileOutputStream fileOutputStream = new FileOutputStream(file2);  //这里就是把更新的apk进行替换了try {byte[] bArr = new byte[102400];while (true) {int read = inputStream.read(bArr);if (read == -1) {break;}fileOutputStream.write(bArr, 0, read);}fileOutputStream.close();if (inputStream != null) {inputStream.close();}} catch (Throwable th) {try {fileOutputStream.close();} catch (Throwable th2) {th.addSuppressed(th2);}throw th;}}zipFile.close();hookApkPath(file.getAbsolutePath(), file2.getAbsolutePath());  //native函数} catch (IOException e) {throw new RuntimeException(e);}} catch (Throwable unused) {System.err.println("Load SignatureKiller library failed");}}

hookApkPath

__int64 __fastcall Java_bin_mt_signature_KillerApplication_hookApkPath(JNIEnv *a1,jclass a2,const char *str,const char *str2)
{::str = (*a1)->GetStringUTFChars(a1, str, 0LL);::str2 = (*a1)->GetStringUTFChars(a1, str2, 0LL);xhook_register(".*\\.so$");                   // 这里是正则表达式:.*:表示任意字符(.)出现任意次数(*),反斜杠 \\ 是转义字符,用来匹配字面上的点号 '.' ,单个的.表示匹配任意单个字符,所以要加上\\转义字符xhook_register(".*\\.so$");xhook_register(".*\\.so$");xhook_register(".*\\.so$");return xhook_refresh(0LL);
}

这里还真全是去注册加载so了,就是不知道为什么要去注册四次,看起来这些也就是一个注册的native

killPM

 private static void killPM(final String str, String str2) {final Signature signature = new Signature(Base64.decode(str2, 0)); // 原始的packInforfinal Parcelable.Creator creator = PackageInfo.CREATOR;try {findField(PackageInfo.class, "CREATOR").set(null, new Parcelable.Creator<PackageInfo>() { // from class: bin.mt.signature.KillerApplication.1/* JADX WARN: Can't rename method to resolve collision */@Override // android.os.Parcelable.Creatorpublic PackageInfo createFromParcel(Parcel parcel) {Signature[] apkContentsSigners;PackageInfo packageInfo = (PackageInfo) creator.createFromParcel(parcel);if (packageInfo.packageName.equals(str)) {if (packageInfo.signatures != null && packageInfo.signatures.length > 0) {packageInfo.signatures[0] = signature;}if (Build.VERSION.SDK_INT >= 28 && packageInfo.signingInfo != null && (apkContentsSigners = packageInfo.signingInfo.getApkContentsSigners()) != null && apkContentsSigners.length > 0) {apkContentsSigners[0] = signature;}}return packageInfo;}/* JADX WARN: Can't rename method to resolve collision */@Override // android.os.Parcelable.Creatorpublic PackageInfo[] newArray(int i) {return (PackageInfo[]) creator.newArray(i);}});if (Build.VERSION.SDK_INT >= 28) {HiddenApiBypass.addHiddenApiExemptions("Landroid/os/Parcel;", "Landroid/content/pm", "Landroid/app");}try {Object obj = findField(PackageManager.class, "sPackageInfoCache").get(null);obj.getClass().getMethod("clear", new Class[0]).invoke(obj, new Object[0]);} catch (Throwable unused) {}try {((Map) findField(Parcel.class, "mCreators").get(null)).clear();} catch (Throwable unused2) {}try {((Map) findField(Parcel.class, "sPairedCreators").get(null)).clear();} catch (Throwable unused3) {}} catch (Exception e) {throw new RuntimeException(e);}}
}

这里传入的str2是软件里面的证书签名

MIIEqDCCA5CgAwIBAgIJAJNurL4H8gHfMA0GCSqGSIb3DQEBBQUAMIGUMQswCQYDVQQGEwJVUzET\nMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5k\ncm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYT\nYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODAyMjkwMTMzNDZaFw0zNTA3MTcwMTMzNDZaMIGUMQsw\nCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQ\nMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG\nCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCC\nAQgCggEBANaTGQTexgskse3HYuDZ2CU+Ps1s6x3i/waMqOi8qM1r03hupwqnbOYOuw+ZNVn/2T53\nqUPn6D1LZLjk/qLT5lbx4meoG7+yMLV4wgRDvkxyGLhG9SEVhvA4oU6Jwr44f46+z4/Kw9oe4zDJ\n6pPQp8PcSvNQIg1QCAcy4ICXF+5qBTNZ5qaU7Cyz8oSgpGbIepTYOzEJOmc3Li9kEsBubULxWBjf\n/gOBzAzURNps3cO4JFgZSAGzJWQTT7/emMkod0jb9WdqVA2BVMi7yge54kdVMxHEa5r3b97szI5p\n58ii0I54JiCUP5lyfTwE/nKZHZnfm644oLIXf6MdW2r+6R8CAQOjgfwwgfkwHQYDVR0OBBYEFEhZ\nAFY9JyxGrhGGBaR0GawJyowRMIHJBgNVHSMEgcEwgb6AFEhZAFY9JyxGrhGGBaR0GawJyowRoYGa\npIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRh\naW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5k\ncm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJAJNurL4H8gHfMAwGA1Ud\nEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHqvlozrUMRBBVEY0NqrrwFbinZaJ6cVosK0TyIU\nFf/azgMJWr+kLfcHCHJsIGnlw27drgQAvilFLAhLwn62oX6snb4YLCBOsVMR9FXYJLZW2+TcIkCR\nLXWG/oiVHQGo/rWuWkJgU134NDEFJCJGjDbiLCpe+ZTWHdcwauTJ9pUbo8EvHRkU3cYfGmLaLfgn\n9gP+pWA7LFQNvXwBnDa6sppCccEX31I828XzgXpJ4O+mDL1/dBd+ek8ZPUP0IgdyZm5MTYPhvVqG\nCHzzTy3sIeJFymwrsBbmg2OAUNLEMO6nwmocSdN2ClirfxqCzJOLSDE4QyS9BAH6EhY6UFcOaE0=\n

对于这里的证书的内容:

这些扩展名代表的是不同类型的证书文件格式,具体如下:.cer 或 .crt:
这两种扩展名通常用于 X.509 证书文件,存储公钥、证书持有者信息、签名算法等。文件可以是 DER 编码 或 PEM 编码 格式,具体取决于编码方式。.cer:可以包含公钥证书或整个证书链。
.crt:通常用于服务器端证书,或者包含 CA 签发的证书。
.p7b 或 .p7c:
这些文件扩展名表示 PKCS #7 文件格式,通常用于存储证书链、证书撤销列表(CRL)等。文件以 Base64 编码的形式存储。PKCS #7 格式用于签名和加密数据,常用于电子邮件、证书链等的传输。.p7m:
这个扩展名用于 PKCS #7 格式的邮件签名文件。通常用于电子邮件的签名或加密邮件。.p7s:
这个扩展名也是 PKCS #7 格式的一部分,主要用于邮件签名,包含签名数据。.swz:
这是一个不太常见的扩展名,可能与某些特殊应用程序的证书有关。.rsa:
这个扩展名通常与 RSA 密钥相关,但它也可能用于存储与 RSA 相关的证书或私钥。.crl:
证书撤销列表(Certificate Revocation List,CRL)文件,用于列出已被撤销的证书。它用于确保没有被撤销的证书被接受。.der:
DER(Distinguished Encoding Rules) 是一种二进制格式,通常用于存储证书、私钥或公钥。它不同于 PEM 格式,PEM 格式是 Base64 编码的文本格式。.der 文件通常用于存储证书链和证书本身。3. MIME 类型:
MIME 类型:application/pkix-cert,是用于描述与 PKI(公钥基础设施)相关的文件的 MIME 类型。该 MIME 类型通常用于 HTTP 请求/响应中,表示文件包含数字证书。
            findField(PackageInfo.class, "CREATOR").set(null, new Parcelable.Creator<PackageInfo>() { // from class: bin.mt.signature.KillerApplication.1/* JADX WARN: Can't rename method to resolve collision */@Override // android.os.Parcelable.Creatorpublic PackageInfo createFromParcel(Parcel parcel) {Signature[] apkContentsSigners;PackageInfo packageInfo = (PackageInfo) creator.createFromParcel(parcel);if (packageInfo.packageName.equals(str)) {if (packageInfo.signatures != null && packageInfo.signatures.length > 0) {packageInfo.signatures[0] = signature;}if (Build.VERSION.SDK_INT >= 28 && packageInfo.signingInfo != null && (apkContentsSigners = packageInfo.signingInfo.getApkContentsSigners()) != null && apkContentsSigners.length > 0) {apkContentsSigners[0] = signature;}}return packageInfo;}

这里是证书替换的关键位置

先看看这里在干嘛

public static final @android.annotation.NonNull Parcelable.Creator<PackageInfo> CREATOR= new Parcelable.Creator<PackageInfo>() {@Overridepublic PackageInfo createFromParcel(Parcel source) {return new PackageInfo(source);}@Overridepublic PackageInfo[] newArray(int size) {return new PackageInfo[size];}

这里是看着有点麻烦,去查了一下其实也就是 object name = new object

Parcelable是一个接口,用于将对象序列化和反序列化,以便能够通过 IntentBundle 在不同组件(如 ActivityService)之间传递数据。Parcelable.Creator<T> 是一个辅助接口,定义了如何通过 Parcel 对象来创建类型为 T 的对象。所以Creator是一个接口的接口,接受一个类来创建对应类的新实例。所以其实这里接受一个 new 一个 PackageInfo类的Parcelable接口类下的辅助接口Creator的PackageInfo实例 然后得到了对应的对象,然后进行了对应的部分接口的实现

每个实现了 Parcelable 接口的类(例如 PackageInfo)通常会自动生成一个静态的 CREATOR 字段,类型为 Parcelable.Creator,负责:

  • 反序列化:从 Parcel 中读取数据,并创建该类的对象。
  • 数组创建:创建对象数组。

对于 CREATOR这个字段是CREATORParcelable.Creator<PackageInfo> 类型的字段,负责通过 Parcel 创建 PackageInfo 对象。通常,这个字段是由 PackageInfo 类自动生成的,但通过反射,可以将其替换为自定义实现,从而改变 PackageInfo 的创建逻辑。

意思也就是在上面实例了自定义的PackageInfo类会自动生成CREATOR字段,所以实现了字段的替换

                    Signature[] apkContentsSigners;PackageInfo packageInfo = (PackageInfo) creator.createFromParcel(parcel);//原始packageInfoif (packageInfo.packageName.equals(str)) {if (packageInfo.signatures != null && packageInfo.signatures.length > 0) {packageInfo.signatures[0] = signature;//替换

之后的代码就是去清理缓存之类的了

if (Build.VERSION.SDK_INT >= 28) {HiddenApiBypass.addHiddenApiExemptions("Landroid/os/Parcel;", "Landroid/content/pm", "Landroid/app");}

这里是HiddenApiBypass.addHiddenApiExemptions利用这个方法去获取了隐藏的API来使用。

至于里面源码中的获取包名,类名之类的操作就放后面了,虽然大相径庭但是可以增加一下代码能力

private static String getApkPath(String str) {String str2;try {BufferedReader bufferedReader = new BufferedReader(new FileReader("/proc/self/maps"));do {String readLine = bufferedReader.readLine();//  /proc/self/maps  readLine  if (readLine != null) {String[] split = readLine.split("\\s+");  //这里按照空格去分割开了str2 = split[split.length - 1];//这里索引下就是对于的路径,其中是由apk的路径的} else {bufferedReader.close();return null;}} while (!isApkPath(str, str2));bufferedReader.close();return str2;} catch (Exception e) {throw new RuntimeException(e);}}
    private static File getDataFile(String str) {String name = Environment.getExternalStorageDirectory().getName(); // /storage/emulated/0if (name.matches("\\d+")) {File file = new File("/data/user/" + name + "/" + str); // /data/user/storage/emulated/0/bbs.shell  或者  /data/user/0/bbs.shellif (file.canWrite()) {return file;}}return new File("/data/data/" + str);}
   private static boolean isApkPath(String str, String str2) {if (str2.startsWith("/") && str2.endsWith(".apk")) {String[] split = str2.substring(1).split("/", 6);int length = split.length;if (length == 4 || length == 5) {if (split[0].equals("data") && split[1].equals("app") && split[length - 1].equals("base.apk")) {return split[length - 2].startsWith(str);}if (split[0].equals("mnt") && split[1].equals("asec") && split[length - 1].equals("pkg.apk")) {return split[length - 2].startsWith(str);}} else if (length == 3) {if (split[0].equals("data") && split[1].equals("app")) {return split[2].startsWith(str);}} else if (length == 6 && split[0].equals("mnt") && split[1].equals("expand") && split[3].equals("app") && split[5].equals("base.apk")) {return split[4].endsWith(str);}}return false;}
/*
检测的结果就是
/data/app/com.example.myapp-1/base.apk
/mnt/asec/com.example.myapp/pkg.apk
/mnt/expand/app/com.example.myapp/base.apk
*/
    private static Field findField(Class<?> cls, String str) throws NoSuchFieldException {try {Field declaredField = cls.getDeclaredField(str);declaredField.setAccessible(true);return declaredField;} catch (NoSuchFieldException e) {while (true) {cls = cls.getSuperclass();if (cls == null || cls.equals(Object.class)) {break;}try {Field declaredField2 = cls.getDeclaredField(str);declaredField2.setAccessible(true);return declaredField2;} catch (NoSuchFieldException unused) {}}throw e;}}
//这里的反射调用字段就很正常了

同时,最后这里的 URL 还是一个 GitHub上面的,不知道是作者写的软件还是借用上去的

public static final String URL = "https://github.com/L-JINBIN/ApkSignatureKillerEx";

这里面就是去签功能的软件,感觉证书是从这里来的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/846198.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

低资源部署 KubeSphere 4.1.2:2 核 4G 极简云原生实战

KubeSphere V4已经开源一段时间了,推出了全新的 KubeSphere 架构:KubeSphere LuBan,它构建在 K8s 之上,支持高度可配置和可扩展。该版本只安装KubeSphere Core核心组件,即可实现KubeSphre Web控制台的使用。前几天在2核4G的VM虚拟机体验了一下。安装过程极快,服务非常精简…

Leangoo助力医药行业项目降本增效

医药行业痛点诸多,需要解决供应链管理、生物技术、医疗器械研发生产、医疗保健服务流程等问题。Leangoo 通过看板功能,实现各领域的信息共享、流程优化、协同增效,提升效率与质量,推动医药行业整体进步与发展。医药行业痛点诸多,制药研发周期长、生物技术创新协同难、医疗…

3274. 检查棋盘方格颜色是否相同

给你两个字符串 coordinate1 和 coordinate2,代表 8 x 8 国际象棋棋盘上的两个方格的坐标。 以下是棋盘的参考图。如果这两个方格颜色相同,返回 true,否则返回 false。 坐标总是表示有效的棋盘方格。坐标的格式总是先字母(表示列),再数字(表示行)。 示例 1: 输入: co…

看板管理:团队协作的秘密武器是什么?

看板视图是什么? 看板(Kanban)最早源自日本的制造业,尤其是丰田生产方式中的看板系统,它是一个通过视觉化管理工作流的系统。随着时间的推移,Kanban从生产线逐渐渗透到了项目管理、软件开发和其他领域,成为一种高效的工作和任务管理工具。在今天,看板视图指的是一种用来…

NetCore3.1 TCP服务之BeetleX

十年河东,十年河西,莫欺少年穷 学无止境,精益求精 1、安装包 2、server端using BeetleX; using BeetleX.EventArgs; using System;namespace BTcp {class Program : BeetleX.ServerHandlerBase{static int Number = 0;private static BeetleX.IServer mServer;static void M…

【详细教程】如何下载新东方在线上面已购买的视频课程

前言:很多同学都想知道新东方在线上的视频课程怎么下载,但是新东方在线上面已购买的视频课程是不提供直接下载方式的,所以下面就教大家如何用学无止下载器下载新东方在线上面已购买的视频课程。防止课程过期后就再也无法观看了,保存到本地就可以永久观看学习! 一、电脑网页…

攻防世界:Web习题之 get_post

攻防世界:Web习题之 get_post 题目内容 https://adworld.xctf.org.cn/challenges/list题目首先需要我们用GET方式提交一个名为a,值为1的变量:提交成功之后我们需要继续用POST方式随便提交一个名为b,值为2的变量:思路 我们可以使用python的requests包来完成这些操作 代码 imp…

评价:几款办公软件为设计团队协作效率注入澎湃动力的真相披露!

在当今竞争激烈、节奏飞快的设计行业,高效的团队协作是项目成功交付的关键因素。对于全 J 人(MBTI 性格类型中倾向于计划、组织和控制的人群)设计团队而言,他们对办公软件的功能性、有序性和协作性有着更高的要求。合适的办公软件不仅能够优化工作流程,还能极大地提升团队…

智慧园区算法视频分析服务器车辆拥堵检测:安防设备中的网络参数科普

在探讨视频智能分析系统的广泛应用于网络安防设备的核心参数时,不可避免地要深入了解其背后的技术支撑与配置细节。这一系统,凭借其强大的视频接入与查看、智能分析、任务调度等功能,已经在工厂、工地、社区等多个场景中展现出了卓越的性能与价值。而网络安防设备,作为这一…

上天入地,智能诊断,多语言支持,璞华IETM打造产品技术信息管理极致用户体验

在当今快节奏的商业环境中,不管是制造商,还是服务提供商都面临着前所未有的挑战:如何快速创建并全生命周期管理产品技术信息成为了业务成败的关键。为了应对这一挑战,高效、便捷、智能的产品技术信息管理解决方案显得尤为重要。IETM(Interactive Electronic Technical Man…

Matlab2023a安装arduino硬件支持包记录

matlab2023a破解版,安装并使用硬件支持包的问题记录安装硬件支持包参考教程: 账号自行免费注册即可https://blog.csdn.net/Wakatipu1734/article/details/127118473下载好的文件如下 接着复制archives到指定目录,期中教程有一个问题: 在下载好安装包后打开应该是install_sup…

GBase8s执行sql脚本报错 -34389 Illegal character has been found in the input string

本人在执行使用GBase8s数据库执行sql脚本时,添加中文注释时报错 -34389 Illegal character has been found in the input string查看了GBase社区的处理方法,并没有解决问题。既然是SQL中存在非法字符,那么也有可能是数据库和sql脚本的字符集不匹配导致的。查看字符集确实不匹…