首页
社区
课程
招聘
[原创]如何在OLLVM混淆中探寻算法一之libyzwg.so算法分析
发表于: 2025-11-25 21:06 1491

[原创]如何在OLLVM混淆中探寻算法一之libyzwg.so算法分析

2025-11-25 21:06
1491

随着移动安全攻防的激进,裸奔的so出场率越来越少,取而代之的是基于OLLVM框架魔改的混淆的so.标题为了避显,延长文章存活时间,此篇以分析算法为主.文章内容仅供学习,切勿用于违法犯罪.
注:此文是我从vx公众号发布后复制过来的,可能有些图片,代码块会有点显示的问题,请移步我的原文地址:

也欢迎各位师傅关注我的公众号,不定期分享算法还原文章.

图片描述
读者不难发现,部分的接口的表单中以及响应数据都做了对应的反爬操作,表单中对请求参数做了加密以及签名,响应数据也做了加密处理.

多次抓包发现,"V3.0"是一个固定的前缀,猜测是算法版本,后面拼接32位的16进制.

从密文中分析不出有用的线索.

从密文中分析不出有用的线索.

跑一遍算法自吐,发现并没有实质的线索,那么加密逻辑可能在native层实现.
笔者通过hook NewStringUTF来快速定位,当然也可以选择反编译apk做关键字的检索,或者hook hashmap这种常见的数据类型,定位的方法很多,这里不做过多的赘述了.

相信看了我之前写的文章的读者们,定位这一步应该不成问题,笔者这里就不带着去定位了,把文章的篇幅留给算法分析这一块,笔者贴一下对应的native方法信息.

sig

sp

响应解密

com.twl.signer.YZWG

com.twl.signer.YZWG

com.twl.signer.YZWG

native方法

private static native byte[] nativeSignature(byte[] bArr, String str);

private static native String nativeEncodeRequest(byte[] bArr, String str);

private static native nativeDecodeContent([BLjava/lang/String;III)[B

在yzwg.so偏移

0x21864

0x209a4

0x24dc8

参数解释

bArr是表单参数进行url编码结果,str是null

bArr是表单参数进行url编码结果,str是null

参数1为加密的响应数据字节数组,参数二为null,参数三-五为0,1,0

运行一下,发现没有那么幸运,这回需要补一下环境才能正常运行.
图片
鼠标点击AbstractJni.java:103蓝色字体进入AbstractJni文件内.
图片
将鼠标对应位置的这一块方法给扣到我们自己写的架子中.
图片
然后做一点小小的改造.
图片
需要注意,我们自己写的类需要继承自AbstractJni.不然无法重写getStaticObjectField.
图片
对于此方法签名,他需要返回这种类型的静态字段Landroid/content/Context;
com/twl/signer/YZWG->gContext:Landroid/content/Context;
我们就给他返回一个这个类型的.
@Override
public DvmObject getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) { switch (signature) { case "com/twl/signer/YZWG->gContext:Landroid/content/Context;": return vm.resolveClass("android/content/Context").newObject(null); } return super.getStaticObjectField(vm,dvmClass,signature); } 接着运行,又报错 图片 此方法是根据UID(用户ID)获取对应的应用程序包名数组. 可以这样补 @Override public DvmObject callObjectMethod(BaseVM vm, DvmObject dvmObject, String signature,VarArg varArg) { System.out.println("[callObjectMethod call]:::::signature=====>"+signature); switch (signature) { case "android/content/pm/PackageManager->getPackagesForUid(I)[Ljava/lang/String;": int uid = varArg.getIntArg(0); //System.err.println("uid:"+uid); return new ArrayObject(new StringObject(vm, vm.getPackageName())); } return super.callObjectMethod(vm,dvmObject,signature,varArg); } 接着继续运行,又报错了 图片 这个可以这样补 @Override public int callIntMethod(BaseVM vm, DvmObject dvmObject, String signature, VarArg varArg) {
switch (signature) {
case "java/lang/String->hashCode()I":
System.out.println("java/lang/String->hashCode()I enter");
String str = dvmObject.getValue().toString();
return str.hashCode();
}
return super.callIntMethod(vm,dvmObject,signature,varArg);
}
补完发现没有别的报错了,接下来我们调用下目标方法.
图片
//主动调用方法
//sp
public String nativeEncodeRequest(byte[] bArr, String str){
//调用静态native方法
DvmObject<?> result = YZWG.callStaticJniMethodObject(
emulator,
"nativeEncodeRequest([BLjava/lang/String;)Ljava/lang/String;",
bArr,
str
);
//获取并返回结果
return (String) result.getValue();
}

//sig
public byte[] nativeSignature(byte[] bArr, String str){
//调用静态native方法
DvmObject<?> result = YZWG.callStaticJniMethodObject(
emulator,
"nativeSignature([BLjava/lang/String;)[B",
bArr,
str
);
//获取并返回结果
return (byte[]) result.getValue();
}

//decode content
public byte[] nativeDecodeContent(byte[] bArr, String str, int i11, int i12, int i13){
//调用静态native方法
DvmObject<?> result = YZWG.callStaticJniMethodObject(
emulator,
"nativeDecodeContent([BLjava/lang/String;III)[B",
bArr,
str,
i11,
i12,
i13
);
//获取并返回结果
return (byte[]) result.getValue();
}
unidbg结果需要和真机对比,看看是否一致.
完整unidbg代码如下
package com.bosszp;

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.;
import com.github.unidbg.linux.android.dvm.api.SystemService;
import com.github.unidbg.linux.android.dvm.array.ArrayObject;
import com.github.unidbg.memory.Memory;
import java.io.
;
import java.nio.charset.StandardCharsets;

public class Boss_Signer extends AbstractJni{

}

图片
对比真机
图片
结果一致,没有问题,响应解密出来也正常,不是乱码之类的东西.
0x7-Ida静态分析+Unidbg下断点还原算法:
0x01-sig(加盐md5算法):
由于nativeSignature方法返回值是字节数组,可以以JNIEnv->SetByteArrayRegion为切入点,ida按g跳转此偏移f5看看上下文.
图片
通过分析可得,v28是v27的长度,而v29是v27,那么研究对象就是这个v27是怎么来的,他是通过一个sub_1C38C方法调用所返回的,我们可以在这个下断点在这个方法.
图片
void sigDebug(){
emulator.attach().addBreakPoint(module,0x1C38C);
}
图片
可以写出方法的形式方便管理.
这里ida识别的有点问题,应该是两个参数的,ida识别成了一个,鼠标放在这个位置,按一下f5,ida即可重写识别.
图片
运行我们搭好的架子,成功断下
mx0查看寄存器值,也就是我们第一个参数
图片
第二个参数是一个数值,是mx0这个值的长度,不是地址,不能通过mx1这样查看,不然会报错.
图片
通过这样的命令即可查看第一个参数完整的值了
mx0 0x3c9
拿到cyberchef里面from hexdump一下
可以发现前面是我们传入的签名数据,后面的32位不知道是啥
图片
a308f3628b3f39f7d35cdebeb6920e21
我们之前也有做过猜想,sig的值有可能是md5加密某个数据,这里我们也来验证一下.
图片
图片
发现是能够对的上的,现在我们未知的是这个32位的数据是从哪来的,是固定的还是动态的呢?我们通过改变签名数据发现,这个值不会变化,而且代入到我们真机hook的数据中也能签名一致,那么我们姑且相信他是固定的,读者也可以自行研究研究.
sig的算法是"V3.0"的前缀拼接md5加密urlpath+请求参数,至此sig算法分析结束.
0x02-sp(标准base64+标准rc4+魔改lz4压缩):
sp是调用的nativeEncodeRequest方法生成结果,返回值是字符串,调用NewStringUTF转换java和c层数据,这个可以是我们的切入点,上篇文章也有讲过,这篇我们换一个思路.
图片
通过搜索base64编码表来定位,
打开Strings这一栏
图片
搜索一下base64的码表
图片
发现确实有,点击跳转过去,鼠标放在上面按x查找交叉引用
图片
引用的位置都指向sub_29E90这个函数,f5反编译看看
图片
发现代码中有很多if-else,cfg流程图中块与块之间的联系变的很复杂,静态分析很难看出来块与块之间的联系,像这种是做了ollvm的混淆处理的,抗ida的静态分析,既然他用到了base64编码表,那么这个函数有没有可能是实现base64编码逻辑的呢?
我们可以在此函数下个断点看看,如果断住了,是不是就说明sp参数有用到这个算法呢?
emulator.attach().addBreakPoint(module,0x29E90);
sub_29E90(_BYTE *a1, __int64 a2, int a3)
这个函数有三个参数,我们看看分别是什么
图片
x0,x1是内存地址,而x2是x1这块内存地址的占用大小.
通过命令查看发现,这块内存地址什么都没有,这其实是个内存缓冲区,函数离开后会将结果写入到此处
mx0
图片
mx1 0x2b1
图片
x1这块内存地址看着不像我们的明文,对其进行标准base64编码看看与加密结果一致不一致.
图片
图片
对比之后,眼光好一点的读者,可以发现,base64码表做了一点小的改动,
/ ---> _

图片
[0x040000000][0x040020ff4][libyzwg.so][0x020ff4]
ida跳转到这个偏移位置
0x020ff4
图片
第二个参数是我们需要研究的对象,也就是这个ptr,他来自于sub_2E91C这个函数,ptr应该是内存的缓冲区,通过这个函数执行后写入数据到这块内存的.
图片
对sub_2E91C下断点看看
emulator.attach().addBreakPoint(module,0x2E91C);
图片
sub_2E91C函数伪C代码
图片
第一个参数
图片

第二个参数
图片
第三个跟第二个相同,第四个是数据长度.
第二个参数看起来有点像我们的明文,但是不完全是.
通过分析sub_2E91C函数的伪C代码,我们发现这是一个rc4算法.
图片
这个函数是rc4主要的加密/解密函数,但是还有s盒的初始化算法,在他的上一个函数.
图片
v62就是对应的s盒.
s对应的是key.
v53是key的长度.
图片
下个断点看看key是什么.
emulator.attach().addBreakPoint(module,0x2E680);

图片
图片
发现其实是我们之前分析的sig的盐值.
拿到网站上面测试一下,看看是否是标准的,验证我们的猜想.
图片
图片
确实就是标准的,现在离成功又近了一步.
我们的研究对象现在是这个跟明文很像的东西是个啥.
通过查找交叉引用
图片
发现来自上面那个函数.
图片

那就下个断点看看情况.
emulator.attach().addBreakPoint(module,0x1D444);
图片
第一个参数是我们的明文数据,第三个是明文数据的长度
图片
第二个是一个内存的缓冲区
图片
sub_1D444函数伪C代码

图片
根据频繁出现的lz4字眼,猜测可能跟这个压缩算法有关.
通过互联网搜索LZ4_compress_limitedOutput关键字,发现了一个与lz4相关的开源项目.
图片
e80K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6D9P5U0c8Q4x3V1k6D9P5U0b7`.
也许这个样本使用了开源的框架.但是他的压缩结果显然不符合标准的lz4.
图片
图片
仔细分析伪C的这部分
图片
发现他多分配了24个字节
sub_1D444函数内部

图片
也有对这24字节的写入相关的代码.

或许就改动了压缩的头部魔数.

python实现lz4压缩

import lz4.frame
import lz4.block
def lz4_compress_fast_extstate_python(data, acceleration=1):
# 使用帧压缩(推荐,包含完整的帧头信息)
compressed = lz4.frame.compress(
data,
compression_level=acceleration, # 1=最快,9=最高压缩率
block_linked=True, # 使用链式块
content_checksum=False, # 校验和
block_checksum=False
)
return compressed
def lz4_decompress_python(compressed_data):
"""解压函数"""
return lz4.frame.decompress(compressed_data)

if name == "main":
test_data = b"account=vGMZRH2Mc2yGuEY%3D&client_info=%7B%22version%22%3A%2210%22%2C%22os%22%3A%22Android%22%2C%22start_time%22%3A%221756205896294%22%2C%22resume_time%22%3A%221756205896294%22%2C%22channel%22%3A%2228%22%2C%22model%22%3A%22google%7C%7CPixel+3%22%2C%22dzt%22%3A0%2C%22loc_per%22%3A0%2C%22uniqid%22%3A%223e818bd6-0d55-45de-8aca-44216661d569%22%2C%22oaid%22%3A%225752bf62-1e11-4a54-979a-c2786373ab6c%22%2C%22oaid_honor%22%3A%225752bf62-1e11-4a54-979a-c2786373ab6c%22%2C%22did%22%3A%22DUuJwkteNiOzCkxbSM3SoAheX_4j-JxcaP7dRFV1SndrdGVOaU96Q2t4YlNNM1NvQWhlWF80ai1KeGNhUDdkc2h1%22%2C%22tinker_id%22%3A%22Prod-arm64-v8a-release-13.141.1314110_0812-10-06-09%22%2C%22is_bg_req%22%3A0%2C%22network%22%3A%22wifi%22%2C%22operator%22%3A%22UNKNOWN%22%2C%22abi%22%3A1%7D&curidentity=0&identityType=-1&isWxLogin=false&phoneCode=2598&regionCode=%2B86&req_time=1756207770991&uniqid=3e818bd6-0d55-45de-8aca-44216661d569&v=13.141"
# 压缩
compressed = lz4_compress_fast_extstate_python(test_data, acceleration=1)
print(compressed.hex())
我们可以用python压缩数据得到一份和unidbg的做个对比.

通过两组数据对比,不难发现,这个样本魔改的lz4算法的魔改点是头部的魔数,也就是那24字节,主要的压缩逻辑没有做魔改.

通过对伪C的分析,我们确定了这24个字节的来源

1-8字节: 42 5a 50 42 6c 6f 63 6b 固定 (8字节)
9-16字节:00000000 (固定 4字节) + lz4算法压缩长度(不包含头和尾巴00)(4字节)
17-24字节:数据长度(4字节) + (数据长度 ^ lz4算法计算的结果)(4字节)
这个v9是压缩后的长度,通过python压缩的结果长度,去掉标准的头以及后面填充的00之后的长度就是压缩后的长度.

图片
至此sp算法分析结束.

0x03-响应数据的解密算法(rc4)

貌似无法根据已有的线索去推断,直接trace(踹死)一份汇编执行流.

//trace工具
void traceCode() {
String traceFile = "unidbg-android/src/test/java/com/bosszp/sp_traceCode.log";// 输出的路径
PrintStream traceStream = null;// 打印流
try {
traceStream = new PrintStream(new FileOutputStream(traceFile), true);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
// traceCode 对代码进行监控
emulator.traceCode(module.base, module.base + module.size).setRedirect(traceStream);
}
在执行之前进行trace

图片
将trace的日志丢到010editor里面分析.

nativeDecodeContent最终返回的是byte[]类型我们把它转成了字符串,这里我们需要再把字符串转成hex在010editor里面进行检索.
图片
00000000 7b 22 63 6f 64 65 22 3a 30 2c 22 6d 65 73 73 61 |{"code":0,"messa|
00000010 67 65 22 3a 22 53 75 63 63 65 73 73 22 2c 22 7a |ge":"Success","z|
00000020 70 44 61 74 61 22 3a 7b 7d 7d |pData":{}}|
根据现有的信息,我们总结一下

因:
[-10, 114, 76, 25, 44, -37, 100, 101, -128, -89, 82, -77, -46, -79, -105, 125, -105, -32, -97, 119, 58, 83, -13, -111, -30, -102, -103, -4, -64, 116, 71, 21, -99, 2, -95, 113, -79, 114, 8, 37, 105, 120]
转16进制:
F6 72 4C 19 2C DB 64 65 80 A7 52 B3 D2 B1 97 7D 97 E0 9F 77 3A 53 F3 91 E2 9A 99 FC C0 74 47 15 9D 02 A1 71 B1 72 08 25 69 78

果:
[123, 34, 99, 111, 100, 101, 34, 58, 48, 44, 34, 109, 101, 115, 115, 97, 103, 101, 34, 58, 34, 83, 117, 99, 99, 101, 115, 115, 34, 44, 34, 122, 112, 68, 97, 116, 97, 34, 58, 123, 125, 125]
转16进制:
7b 22 63 6f 64 65 22 3a 30 2c 22 6d 65 73 73 61 67 65 22 3a 22 53 75 63 63 65 73 73 22 2c 22 7a 70 44 61 74 61 22 3a 7b 7d 7d

在汇编执行流中按单个字节搜索,然后定位由果溯因,数据溯源.

搜索的结果有156个

图片
定位的话,要花点时间,一个一个分析下断点,笔者定位到的最终位置是第151个

[12:44:45 923][libyzwg.so 0x02e9bc] [4d682f38] 0x4002e9bc: "strb w13, [x2, x15]" w13=0x7b x2=0x403d4000 x15=0x0 => w13=0x7b
010editor里面搜索这个偏移

0x02e9bc
图片
与结果是对的上的,那就是这个位置了,ida跳转到此处看看上下文.

图片
可以发现是我们刚刚分析过的rc4,用原来的key解密一下看看能否得到结果

a308f3628b3f39f7d35cdebeb6920e21
图片
图片
发现能够正常的解密

至此libyzwg.so的算法就分析完了.

这里额外提几个点,经过我的测试,新版本的算法差别不大,几乎没有改变.

新版本把64位的so换成了32位,算法没变,key也没变,同一个数据,修改apk路径和so路径的两个unidbg得到的结果一致.

图片
另外就是响应解密那个部分,有些接口,返回的数据过大会进行lz4压缩在rc4加密,lz4算法跟前面分析的差不多,知道压缩就能逆推解压的算法.

0x8-结语:

感谢各位读者的收听,如文章有分析不妥或错误的地方,欢迎各位师傅斧正,笔者本着是想一口气写完三个样本的分析,但是考虑到篇幅可能过长,所以循序渐进的来讲解案例,逐帧学习.

下期精彩:

libpdd_secure.so算法分析

libjdgs.so算法分析

aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3MvQUJaNkxxc19wMDZIcnRoR2gxMWxIZw==
aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3MvQUJaNkxxc19wMDZIcnRoR2gxMWxIZw==
aHR0cHM6Ly93d3cud2FuZG91amlhLmNvbS9hcHBzLzYyMDIyMjIvaGlzdG9yeV92MTMxNDExMA==
aHR0cHM6Ly93d3cud2FuZG91amlhLmNvbS9hcHBzLzYyMDIyMjIvaGlzdG9yeV92MTMxNDExMA==
v13.141
v13.141
function hook_newStr() {
    var symbols = Module.enumerateSymbolsSync("libart.so");
    var addrNewStringUTF = null;
    for (var i = 0; i < symbols.length; i++) {
        var symbol = symbols[i];
        if (symbol.name.indexOf("NewStringUTF") >= 0 && symbol.name.indexOf("CheckJNI") < 0) {
            addrNewStringUTF = symbol.address;
        }
    }
    if (addrNewStringUTF != null) {
        Interceptor.attach(addrNewStringUTF, {
            onEnter: function (args) {
                var c_string = args[1];
                var dataString = c_string.readCString();
                //筛选结果
                if (dataString.includes("V3.0")) {
                    console.log(dataString);
                    //打印堆栈
                    console.log(Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n');
                    console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
                }
                if (dataString.includes("zwp")) {
                    console.log(dataString);
                    //打印堆栈
                    console.log(Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n');
                    console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
                }
            }
        });
    }
}
function hook_newStr() {
    var symbols = Module.enumerateSymbolsSync("libart.so");
    var addrNewStringUTF = null;
    for (var i = 0; i < symbols.length; i++) {
        var symbol = symbols[i];
        if (symbol.name.indexOf("NewStringUTF") >= 0 && symbol.name.indexOf("CheckJNI") < 0) {
            addrNewStringUTF = symbol.address;
        }
    }
    if (addrNewStringUTF != null) {
        Interceptor.attach(addrNewStringUTF, {
            onEnter: function (args) {
                var c_string = args[1];
                var dataString = c_string.readCString();
                //筛选结果
                if (dataString.includes("V3.0")) {
                    console.log(dataString);
                    //打印堆栈
                    console.log(Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n');
                    console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
                }
                if (dataString.includes("zwp")) {
                    console.log(dataString);
                    //打印堆栈
                    console.log(Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n');
                    console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
                }
            }
        });
    }
}
package com.bosszp;
 
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.array.ArrayObject;
import com.github.unidbg.memory.Memory;
import java.io.*;
import java.nio.charset.StandardCharsets;
 
public class Boss_Signer extends AbstractJni{
    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;
    private final DvmClass YZWG;
 
    //初始化虚拟机
    public Boss_Signer() {
        //创建模拟器实例 (ARM64架构)
        emulator = AndroidEmulatorBuilder.for64Bit()
                .setProcessName("com.xunmeng.pinduoduo")
                .build();
        //获取内存接口
        final Memory memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));  // Android 6.0
        //创建Android虚拟机
        vm = emulator.createDalvikVM(new File("unidbg-android/src/test/resources/apks/BOSS直聘_v13.141.apk"));  // 替换为实际APK路径
        //设置JNI接口
        vm.setJni(this);
        vm.setVerbose(true);  // 打印详细日志
        //加载目标SO库
        DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/resources/example_binaries/arm64-v8a/libyzwg.so"), true);  // 替换为实际SO路径
        dm.callJNI_OnLoad(emulator);  // 调用SO初始化函数
        module = dm.getModule();
        //获取目标类
        YZWG = vm.resolveClass("com/twl/signer/YZWG");
    }
    //入口
    public static void main(String[] args) {
        Boss_Signer signer = new Boss_Signer();
    }
}
package com.bosszp;
 
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.array.ArrayObject;
import com.github.unidbg.memory.Memory;
import java.io.*;
import java.nio.charset.StandardCharsets;
 
public class Boss_Signer extends AbstractJni{
    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;
    private final DvmClass YZWG;
 
    //初始化虚拟机
    public Boss_Signer() {
        //创建模拟器实例 (ARM64架构)
        emulator = AndroidEmulatorBuilder.for64Bit()
                .setProcessName("com.xunmeng.pinduoduo")
                .build();
        //获取内存接口
        final Memory memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));  // Android 6.0
        //创建Android虚拟机
        vm = emulator.createDalvikVM(new File("unidbg-android/src/test/resources/apks/BOSS直聘_v13.141.apk"));  // 替换为实际APK路径
        //设置JNI接口
        vm.setJni(this);
        vm.setVerbose(true);  // 打印详细日志
        //加载目标SO库

[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!

收藏
免费 47
支持
分享
最新回复 (20)
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
谢谢分享,不过图片好像没上传去
2025-11-25 21:47
0
雪    币: 20
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
29o
3
感谢分享
2025-11-25 22:10
0
雪    币: 374
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
mb_asiwnxyv 谢谢分享,不过图片好像没上传去
我是复制的我公众号发布的文章到看雪的,图片和代码块啥的会丢失
原文可以看我公众号发的
aHR0cHM6Ly9tcC53ZWl4aW4ucXEuY29tL3MvQUJaNkxxc19wMDZIcnRoR2gxMWxIZw==
base64解码
2025-11-25 22:12
0
雪    币: 530
活跃值: (2015)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
666
2025-11-26 10:49
0
雪    币: 925
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
1
2025-11-26 13:07
0
雪    币: 51
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
tql
2025-11-26 17:23
0
雪    币: 293
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
666666666
2025-11-27 10:15
0
雪    币: 104
活跃值: (7189)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
tql
2025-11-27 11:27
0
雪    币: 31
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
10

你的帖子非常有用,感谢分享!
2025-11-27 14:22
0
雪    币: 204
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
大佬牛逼 
2025-11-27 15:35
0
雪    币: 1762
活跃值: (1255)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
12
感谢分享
2025-11-27 18:15
0
雪    币: 143
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
13
666
2025-11-27 23:27
0
雪    币: 200
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
14
666
2025-11-28 09:56
0
雪    币: 10
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
15
666
2025-11-28 16:35
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
16
666
2025-11-28 17:40
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
17
666
2025-11-29 08:55
0
雪    币: 5148
活跃值: (1752)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
666
2025-12-13 13:14
0
雪    币: 260
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
19
```1
2025-12-15 02:17
0
雪    币: 8237
活跃值: (4778)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
感谢分享。
6天前
0
雪    币: 205
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
21
666666666
2天前
0
游客
登录 | 注册 方可回帖
返回