前言
本文章仅做移动安全学习交流用途,严禁作其他用途,如果侵犯您的权益请联系我删除。
所用工具: IDA Pro, frida, jadx
分析
该app带有反调试,在libmaoaidesc.so文件中,用dlsym函数可以hook到调用了pthread_create,与某黑盒,bili是一样的,过检测代码
Point
1.sub_64960中的sub_6551C
jadx
在jadx中搜索关键词isBackground

进去看到

按x查看
sign的调用
发现两处,进第二处发现

hook这个方法

经过对比发现

那么可以确定这个就是
sign值,按d查看这个m51046b方法
发现传进了三个参数,第一个是url接口,第二个是固定字符串
wdi4n2t8edr,第三个暂时不知道是怎么生成的,但是大多数时候值为-1,按d进入函数zcvsd1wr2t,发现
IDA
直接用findhash插件搜索,定位到函数sub_64A84

可以看出是一个MD5函数,查看此函数调用

在
sub_6551C函数中,hook该函数,hook代码
function hook_sub_6551C(){
Java.perform(function () {
var a45d = Module.findBaseAddress("libRequestEncoder.so")
a45d = a45d.add(0x6551C)
Interceptor.attach(a45d,{
onEnter:function(arg){
var c_arg1 = arg[1].readCString();
var c_arg2 = arg[2];
console.log("参数1:",(arg[1]),c_arg1);
console.log("参数2:",arg[2],c_arg1);
},
onLeave:function(ret){
console.log("返回值;",(ret),ptr(ret).readCString());//.add(0x1)
console.log();
}
})
});
}
可以得到

/leo-exam/android/photograph/paper/experiencewdi4n2t8edr7816b3677e8e2e81e2db7602f38a223c/leo-exam/android/photograph/paper/experience2e88d87168558127e673cd8596596cae576175548014621528808779773355443228808783155761755132880877428808783288087697480146228808777155761755576175515181532009753200976576175524007312880877713960292596029252880877915288087799602925288087799602925288087761677721628808779715151515401537985928962618979152400731288087155761755480146201828808737985928962880876971533554432288087772216059480146215261897915221605928808776320097532009761677721628808779ab24139cfb96a255c09ab23cccb986c8wdi4n2t8edr
将该参数分为4段:
字段1:/leo-exam/android/photograph/paper/experiencewdi4n2t8edr为接口+固定字符串wdi4n2t8edr,其MD5的值为7816b3677e8e2e81e2db7602f38a223c
字段2:字段1+MD5(字段1)+接口+MD5(字段1+MD5(字段1)+接口)
字段3:576175548014621528808779773355443228808783155761755132880877428808783288087697480146228808777155761755576175515181532009753200976576175524007312880877713960292596029252880877915288087799602925288087799602925288087761677721628808779715151515401537985928962618979152400731288087155761755480146201828808737985928962880876971533554432288087772216059480146215261897915221605928808776320097532009761677721628808779,不知道生成方式
字段4:ab24139cfb96a255c09ab23cccb986c8wdi4n2t8edr,为MD5(字段1+字段2+字段3)+wdi4n2t8edr

在
sub_6551C的下面有一个sub_655E4,hook这个函数发现
其最终返回值和sign值相同。
为了hook到注册的RegisterNatives函数,在过检测代码中修改函数hook_dlopen为
function hook_dlopen() {
var interceptor = Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
{
onEnter: function (args) {
var pathptr = args[0];
if (pathptr !== undefined && pathptr != null) {
var path = ptr(pathptr).readCString();
console.log("[LOAD]", path)
if (path.indexOf("libmsaoaidsec.so") > -1) {
hook_dlsym()
}else if(path.indexOf("libRequestEncoder.so") > -1){
hook_RegisterNatives();
}
}
},
}
)
return interceptor
}
注入启动app,控制台搜索zcvsd1wr2t,得到

即native函数
zcvsd1wr2t对应为sub_0x61bd4
[RegisterNatives] java_class: com.fenbi.android.leo.utils.e name: zcvsd1wr2t sig: (Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String; fnPtr: 0x7af4fb2bd4 module_name: libRequestEncoder.so module_base: 0x7af4f51000 offset: 0x61bd4
hook该函数,其返回值就是sign参数

处理一下参数的打印

发现参数2和参数3和参数4正是在java层中传入的参数。(注:因为参数4时带符号位,因此是-1)

hook代码
function hook_sub_61BD4(){
Java.perform(function () {
let C14561e = Java.use("com.fenbi.android.leo.utils.e");
C14561e["zcvsd1wr2t"].implementation = function (str, str2, i) {
console.log(`C14561e.zcvsd1wr2t is called: str=${str}, str2=${str2}, i=${i}`);
let result = this["zcvsd1wr2t"](str, str2, i);
console.log(`C14561e.zcvsd1wr2t result=${result}`);
return result;
};
var a45d = Module.findBaseAddress("libRequestEncoder.so")
a45d = a45d.add(0x61BD4)
Interceptor.attach(a45d,{
onEnter:function(arg){
var c_arg0 = ptr(arg[0]).readCString();
var c_arg1 = ptr(arg[1]).readCString();
var c_arg2 = ptr(arg[2]).readCString();
var c_arg3 = ptr(arg[3]).readCString();
var c_arg4 = ptr(arg[4]);
console.log("参数0:",arg[0],getAsciiLog(arg[0],16), (arg[0]));
console.log("参数1:",(arg[1]),getAsciiLog(arg[1],16),objectToString(arg[1]));
console.log("参数2:",(arg[2]),getAsciiLog(arg[2],16),objectToString(arg[2]));
console.log("参数3:",(arg[3]),getAsciiLog(arg[3],16),objectToString(arg[3]));
console.log("参数4:",(arg[4]),(arg[4]),(arg[4]));
},
onLeave:function(ret){
console.log("返回值;",ret, objectToString(ret));//.add(0x1)
console.log();
}
})
});
}
2024-10-11
简单分析判断加密流程:
传接口字符串和固定字符串于sub_61BD4,调用五次sub_64960,在每次sub_64960执行中,又调用三次sub_6551C,即一共调用15次sub_6551C,通过hook函数sub_6551C,发现确实调用了15次,那么按照执行顺序,可以知道每次调用对应的是哪个位置。
/leo-exam/android/photograph/paper/experiencewdi4n2t8edr7816b3677e8e2e81e2db7602f38a223c/leo-exam/android/photograph/paper/experience2e88d87168558127e673cd8596596cae5762155480179616288107817733554432288107831657621552028810774288107832881077774801796288107811657621555762155162116320119732011985762155240089828810774720960359396035932881078116288107819603593288107819603593288107801677721628810781471616161640163631869312261916118240089828810716576215548017960212881073631869312288107777183355443228810781221621348017961626191611622162132881078032011973201198167772162881078196be3984368a5948474cd19ec491c759wdi4n2t8edr
对应sign值ea1af8b12973ab36e8d8f5ca59fffc47
/bh5/leo-web-search/search.htmlwdi4n2t8edrabe0f1a802d83316b79e5f1e51d14d56/bh5/leo-web-search/search.html0483ea40bbbcbdb568adcf6f0e9add5d57621564801797132881081677335544322881078713576215617288107842881078728810784748017972881078613576215657621561317133201198320119957621562400898288107851179603594960359428810816132881081696035942881081696035942881078516777216288108165113131313451375099520261916215240089828810713576215648017975172881077509952028810784715335544322881078622162144801797132619162132216214288107853201198320119916777216288108165998389161498beaf124128a56231d3awdi4n2t8edr
对应sign值6aa4b41358961a5b0ec944285507e69b
将此类型值直接MD5可得到sign
分析此参数
第一段:
第一段为接口+固定字符串wdi4n2t8edr
/bh5/leo-web-search/search.htmlwdi4n2t8edr
第二段:
abe0f1a802d83316b79e5f1e51d14d56,为第一段MD5值
第三段:
/bh5/leo-web-search/search.htmlwdi4n2t8edrabe0f1a802d83316b79e5f1e51d14d56/bh5/leo-web-search/search.html,为:第一段+第二段+接口的MD5值
第四段:
/bh5/leo-web-search/search.htmlwdi4n2t8edrabe0f1a802d83316b79e5f1e51d14d56/bh5/leo-web-search/search.html0483ea40bbbcbdb568adcf6f0e9add5d,为:第三段+第三段的MD5值
第五段:
/bh5/leo-web-search/search.htmlwdi4n2t8edrabe0f1a802d83316b79e5f1e51d14d56/bh5/leo-web-search/search.html0483ea40bbbcbdb568adcf6f0e9add5d5762156480179713288108167733554432288107871357621561728810784288107872881078474801797288107861357621565762156131713320119832011995762156240089828810785117960359496035942881081613288108169603594288108169603594288107851677721628810816511313131345137509952026191621524008982881071357621564801797517288107750995202881078471533554432288107862216214480179713261916213221621428810785320119832011991677721628810816,为第四段
第六段:
5998389161498beaf124128a56231d3a,为第四段MD5值
完整为:第五段+第六段+wdi4n2t8edr
现在只需要分析出5762156480179713288108167733554432288107871357621561728810784288107872881078474801797288107861357621565762156131713320119832011995762156240089828810785117960359496035942881081613288108169603594288108169603594288107851677721628810816511313131345137509952026191621524008982881071357621564801797517288107750995202881078471533554432288107862216214480179713261916213221621428810785320119832011991677721628810816的生成即可还原出sign算法
2024-10-12
上文提到
传接口字符串和固定字符串于sub_61BD4,调用五次sub_64960,在每次sub_64960执行中,又调用三次sub_6551C,即一共调用15次sub_6551C,通过hook函数sub_6551C,发现确实调用了15次,那么按照执行顺序,可以知道每次调用对应的是哪个位置。
在第四次调用sub_64960的时候,参数后加了一个不知道的长数组字符串。
sub_61BD4调用了一次sub_64960,而剩余四次对sub_64960的调用在sub_66A1C中,sub_61BD4中调用的sub_64960传入的参数0为toChars,参数1不知道。
在整个sign生成的过程中,执行了5次sub_64960,首次是sub_61BD4中的一次,其余四次似乎出现在了sub_66A1C中,在第四次执行sub_64960时,参数1多了待解密字符串
ida关键代码应该是
std::string::append((int)v33, v20, v21);
sub_64960((__int64)v38, (__int64)v33);
第一句代码作用是将v20的字符串拼接到v33所指字符串的后面,长度为v21,尝试去hook一下这append函数。hook代码如下:
function hook_append(){
Java.perform(function(){
var a45d = Module.findBaseAddress("libRequestEncoder.so")
// 假设 SO 文件路径为 "libyourlibrary.so",以及函数地址
var moduleName = "libRequestEncoder.so";
var symbolName = "_ZNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKcm"; // C++ 的修饰名
Interceptor.attach(Module.findExportByName(moduleName, symbolName), {
onEnter: function (args) {
// args[0] 是第一个参数 _QWORD *a1
// args[1] 是第二个参数 void *src
// args[2] 是第三个参数 unsigned __int64 n
console.log("std::string::append called");
console.log("a1: " + args[0]); // 打印 a1
console.log("src: " + Memory.readUtf8String(args[1])); // 打印 src 内容
console.log("n: " + args[2].toInt32()); // 打印 n 的值
},
onLeave: function (retval) {
console.log("Return value: " + retval); // 打印返回值
}
});
})
}
hook结果

sub_66A1C中一共出现了7次append函数,我们也hook到了7次append函数,且第五次添加了未知字符串,而第五次对应的正好就是上面贴出的伪代码,因此可以确定,v20就是这个未知字符串的地址。
那么现在的任务就是分析v20是怎么来的
2024-10-13
通过对java层hook函数入参的修改,发现当传入接口字符串固定时,在一小段时间内,所有sign值相同,一段时间后,sign值开始变化成另外一个sign,并持续一小段时间。
经过观察发现,这一小段时间是60秒,每60秒内,无论接口字符串是什么,所有未解密参数的值都是相同的,且即使将固定字符串修改为不同的值,所有未解密参数的值也都是相同的,也就是说,未解密字符串的生成与时间有关,与接口字符串和固定字符串无关。
看汇编代码
.text:0000000000066B5C 41 01 89 9A CSEL X1, X10, X9, EQ ; src
CSEL X1, X10, X9, EQ 的意思是:
如果之前的条件(通常是由 CMP 指令产生的)满足 EQ,则 X1 将取 X10 的值。
否则,X1 将取 X9 的值。
这条指令可以用于根据条件来决定赋值给目标寄存器的内容。
再通过代码查看寄存器的值
//在hook_append函数中
console.log('X10:', this.context.x10.readCString()); // X10 寄存器的值
console.log('X9:', this.context.x9.readCString()); // X9 寄存器的值

可以看到,X9寄存器中存放着待解密字符串。那么就是,前面的代码中存在将待解密字符串放到寄存器X9的操作,那么我们可以再hook一下这个sub_66A1C函数,看一下在刚开始调用时X9寄存器是否就已经放着待解密字符串,如果没有,那么待解密字符串的来源就可以锁定在这个sub_66A1C的内部;如果有,那么就需要做另外的分析了。

hook发现其中并没有放着待解密字符串,而是api接口,那么正如前面所说,待解密字符串的来源就锁定在这块代码中了。
sub_61BD4调用了sub_66A1C
sp+0x40,bp-0x80
2024-10-14
sub_67198每次的返回值都为待解密字符串
观察sub_65784函数,其为sub_67198所调用
5762156480179713288108167733554432288107871357621561728810784288107872881078474801797288107861357621565762156131713320119832011995762156240089828810785117960359496035942881081613288108169603594288108169603594288107851677721628810816511313131345137509952026191621524008982881071357621564801797517288107750995202881078471533554432288107862216214480179713261916213221621428810785320119832011991677721628810816
再看这段字符串,其中的28810785为(时间戳-1)/60
2024-10-15
我们这里拿57632614802717162881630877335544322881631116576326118288163042881631128816304748027172881631016576326157632611619163201811320181257632612401358288163021189605435960543528816308162881630896054352881630896054352881630516777216288163082116161616411613053413122619664172401358288163165763261480271711928816313053413122881630471733554432288163102216638480271716261966416221663828816305320181132018121677721628816308来作为例子分析其是如何构造组成的。
首先需要说明的是,此例子参数对应的时间戳为1728978392

这里设a2为-1通过代码*(_QWORD *)timer = (*(_QWORD *)timer + a2) / 60LL;计算后,timer被赋值为28816306。
先定义一些函数
def HIWORD(value):
return (value >> 16) & 0xFFFF
def LOBYTE(value):
return value & 0xFF # 取低8位
def HIBYTE(value):
return (value >> 8) & 0xFF # 右移8位后取低8位
def LOBYTE(w):
return w & 0xFF
伪代码块1

然后将此值赋给
v3,并进行计算,这里的伪代码用Python实现为:
# block 1
timer_0 = 28816306
v3 = timer_0
v167 = timer_0 >> 4
v157 = timer_0 >> 3
v4 = v157 + v167 + ((v157 + v167) >> 4) + ((v157 + v167 + ((v157 + v167) >> 4)) >> 8)
v5 = v4 + HIWORD(v4) + ((13 * (timer_0 - 5 * (v4 + HIWORD(v4)))) >> 6)
print("第一个 => ",v5)
# 输出 => 5763261
伪代码块2

Python实现:
v6 = v3 + (v3 >> 31)
v170 = v6
v160 = v6 >> 3
v7 = (v6 >> 3) + (v6 >> 5) + (((v6 >> 3) + (v6 >> 5)) >> 4)
v8 = v7 + (v7 >> 8) + ((v7 + (v7 >> 8)) >> 16)
v9 = v8 + ((11 * (v6 - 6 * v8)) >> 6)
print("第二个 => ",v9)
# 输出 => 4802717
伪代码块3

Python实现:
v171 = v3 >> 1
v10 = ((((v171 & 0x55555555) + (v3 & 0x55555555)) >> 2) & 0x33333333)+ (((v171 & 0x55555555) + (v3 & 0x55555555)) & 0x33333333)
v11 = (((((v10 >> 4) & 0x7070707) + (v10 & 0x7070707)) >> 8) & 0xF000F)+ ((((v10 >> 4) & 0x7070707) + (v10 & 0x7070707)) & 0xF000F)
v12 = (v11 & 0x1F) + HIWORD(v11)
print("第三块 => ",v12)
# 输出 => 16
伪代码块4

Python实现:
v13 = HIWORD(v3)
v169 = v13
v165 = -v3 & v3
v172 = v165 + v3
if v3 != 0:
if v3 & 0xFFFF:
v13 = v3
if v3 & 0xFFFF:
v14 = 1
else:
v14 = 17
v15 = (v13 & 0xFF) == 0
if (v13 & 0xFF) == 0:
v13 >>= 8
if v15:
v14 |= 8
v16 = (v13 & 0xF) == 0
if (v13 & 0xF) == 0:
v13 >>= 4
if v16:
v14 |= 4
v17 = (v13 & 3) == 0
if (v13 & 3) == 0:
v13 >>= 2 # LOBYTE(v13) = v13 >> 2
if v17:
v14 |= 2
v18 = v14 - (v13 & 1) + 2
else:
v18 = 34
block4_value = ((v172 ^ v3) >> v18) | v172
print("第四块 => ",block4_value)
# 输出 => 28816308
伪代码块5,6

注:这里执行了两遍
std::ostream::operator<<(&v174, v24);,也就是拼接了两次,第5和6是重复的。Python实现:
if ( v3 ):
v19 = v3 << 16
if ( v3 >= 0x10000 ):
v19 = v3
v20 = HIBYTE(v19) == 0
if ( not HIBYTE(v19) ):
v19 <<= 8
if ( v20 ):
v21 = (16 * (v3 < 0x10000)) | 8
else:
v21 = 16 * (v3 < 0x10000)
v22 = v19 >> 28 == 0
if ( not (v19 >> 28) ):
v19 *= 16
if ( v22 ):
v21 |= 4
v23 = v19 >> 30 == 0
if ( not (v19 >> 30) ):
v19 *= 4
if ( v23 ):
v21 |= 2
v24 = v21 + ((v19 & 0x80000000) == 0)
else:
v24 = 32
print("第五块 => ",v24)
print("第六块 => ",v24)
# 输出 => 7
# 输出 => 7
伪代码块7

Python实现:
v25 = (v3 - 1) | ((v3 - 1) >> 1) | (((v3 - 1) | ((v3 - 1) >> 1)) >> 2)
v26 = v25 | (v25 >> 4) | ((v25 | (v25 >> 4)) >> 8)
v159 = (v26 | HIWORD(v26)) + 1
print("第七块 => ",v159)
# 输出 => 33554432
伪代码块8,9,10

v12和v5都是已知值,只需关注v33 | v34Python实现:
v27 = v3 + 1
v28 = v3 + 3
v29 = v3 + 2
v30 = (v3 + 3) & (v3 + 1)
v31 = 0x80000000 # 相当于 2^31
while v31 > 1:
if (v31 & v30) != 0:
v34 = (v27 - v31) | (v31 - 1)
if v34 >= v3:
v33 = v3 + 3
break
v33 = (v28 - v31) | (v31 - 1)
if v33 >= v29:
v34 = v3 + 1
break
v31 >>= 1 # 相当于右移一位
# 如果循环结束,计算默认值
if v31 == 1:
v34 = v3 + 1
v33 = v3 + 3
block8_value = v33 | v34
print("第八块 => ",block8_value)
print("第九块 => ",v12)
print("第十块 => ",v5)
# 输出 => 28816311
# 输出 => 16
# 输出 => 5763261
伪代码块11(暂未复现)

伪代码块12

Python实现:
v142 = v170 >> 1
v36 = (v170 >> 1) + (v170 >> 2) + (((v170 >> 1) + (v170 >> 2)) >> 4)
v37 = (v36 + (v36 >> 8) + ((v36 + (v36 >> 8)) >> 16)) >> 3
v135 = v37 + ((v170 - 10 * v37 + 6) >> 4)
print("第十二块 => ",v135)
# 输出 => 2881630
伪代码块13(暂未复现)

Python实现:
伪代码块14

Python实现:
v41 = 0x80000000
while True:
if (v41 & v30) != 0:
v43 = (v27 - v41) | (v41 - 1)
if v43 >= v3:
break # LABEL_59
v42 = (v28 - v41) | (v41 - 1)
if v42 >= v29:
v43 = v3 + 1
break # LABEL_60
v32 = v41 > 1
v41 >>= 1
if not v32:
v43 = v3 + 1
break
v42 = v3 + 3 # After LABEL_59
result = v42 | v43 # After LABEL_60
print("第十四块 => ",result)
# 输出 => 28816311
伪代码块15(暂未复现)

伪代码块16,17

Python实现:
dword_42BE0 = [0, 9, 0x63, 0x3E7, 0x270F, 0x1869F, 0xF423F, 0x98967F, 0x5F5E0FF, 0x3B9AC9FF, 0xFFFFFFFF]
v47 = -2
while True:
v48 = v47 + 2
v47 = v47 + 1
if dword_42BE0[v48] >= v3:
break
print("第十六块 => ",v47)
print("第十七块 => ",v9)
# 输出 => 7
# 输出 => 4802717
伪代码块18(暂未复现)

伪代码块19(暂未复现)

Python实现:
伪代码块20

Python实现:
v54 = v3 >> 2
v55 = v171 + (v3 >> 2)
v56 = v55 + (v55 >> 4) + ((v55 + (v55 >> 4)) >> 8)
v57 = (v56 + (v56 >> 16)) >> 2 # Assuming HIWORD is equivalent to shifting right by 16 bits
v58 = v3 - 5 * v57
v140 = v57 + ((7 * v58) >> 5)
print("第二十块 => ",v140) # Assuming v174 is a file-like object
# 输出 => 5763261
伪代码块21

Python实现:
if v58 <= 4:
v59 = v57
else:
v59 = v57 + 1
if v58 <= 9:
v60 = v59
else:
v60 = v59 + 1
v148 = v60
print("第二十一块",v60)
# 输出 => 5763261
伪代码块22

Python实现:
2024-10-18
伪代码块
中间写着太麻烦了,道理是一样的,这里就省去了,只对伪代码块30进行分析。
伪代码块30

v66.n64_u64[0] = veor_s8((int8x8_t)__PAIR64__(v171, v54), vdup_n_s32(v3)).n64_u64[0];
v67 = v66.n64_u32[1];
v68.n128_u64[0] = veor_s8(vshr_n_u32(v66, 4uLL), v66).n64_u64[0];
v68.n128_u32[2] = v67 ^ (v67 >> 2);
v68.n128_u32[3] = v68.n128_u32[2];
v69 = veorq_s8(vshlq_u32(v68, (uint32x4_t)xmmword_42890), v68);
v70 = vandq_s8(
veorq_s8(vshlq_u32(v69, (uint32x4_t)xmmword_42870), vshlq_u32(v69, (uint32x4_t)xmmword_42850)),
(int8x16_t)xmmword_42910);
v69.n128_u64[0] = vorr_s8((int8x8_t)v70.n128_u64[0], (int8x8_t)vextq_s8(v70, v70, 8uLL).n128_u64[0]).n64_u64[0];
v136 = (v69.n128_u32[0] | v69.n128_u32[1] | ((v69.n128_u32[3] ^ (v69.n128_u32[3] >> 8)) >> 12) & 0x10 | (32 * (((unsigned __int8)(v69.n128_u8[12] ^ v69.n128_u8[13]) ^ (unsigned __int8)((v69.n128_u32[3] ^ (v69.n128_u32[3] >> 8)) >> 16)) & 1))) ^ -(v3 & 1) & 0x3F;
std::ostream::operator<<(&v174, v136);
此代码涉及到向量数组运算,且v66,v68,v69,v70为c++中的联合体。
PAIR64
在IDA的头文件摘录中可以找到
#define __PAIR64__(high, low) (((uint64) (high) << 32) | (uint32)(low))
详细可以看这篇文章,IDA头文件摘录
veor_s8,vdup_n_s32,vshr_n_u32,veorq_s8,vshlq_u32,vandq_s8,vorr_s8,vextq_s8
这些函数在SIMD指令集中可以找到,可以去这个网站找指令集
veor_s8
Bitwise Exclusive OR (vector). This instruction performs a bitwise Exclusive OR operation between the two source SIMD&FP registers, and places the result in the destination SIMD&FP register.
指的是向量按位异或运算。可以用Python来实现
np.logical_xor(a, b)
vdup_n_s32
vdup_n_s32 的作用是将给定的标量(一个 32 位的整数)填充到一个 128 位的向量中的每个位置,具体来说,它会生成一个包含 4 个 32 位元素的向量,而这 4 个元素的值是一样的。
Python实现:
# vdup_n_s32(v3)
np.array([v3,v3,v3,v3])
2024-10-26

(上面有具体代码)
看了好几天,这部分实在是复现不出来,其他部分的都已经实现,如果有能复现出来这段的可以跟我交换一下其他部分的,或者有思路的也可以一起交流一下。

(部分复现代码)
这里给出一个可以用来复现时验证的例子
# 那段代码中会用到的参数
v3 = 28818947
v171 = v3 >> 1
v54 = v3 >> 2
# 有了这些参数后,经过那段代码计算后可以得到v136的值为5
# 注:你可能算出来的结果是0或63,这两个值是不正确的,说明还没有复现成功
关于这四个参数:xmmword_42890,xmmword_42870,xmmword_42850,xmmword_42910
在IDA中为
.rodata:0000000000042890 F8 FF FF FF F8 FF FF FF F8 FF+xmmword_42890 DCB 0xF8, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF
.rodata:0000000000042870 FF FF FF FF EF FF FF FF EE FF+xmmword_42870 DCB 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xEE, 0xFF, 0xFF, 0xFF, 0xEB, 0xFF, 0xFF, 0xFF
.rodata:0000000000042850 EF FF FF FF FF FF FF FF FE FF+xmmword_42850 DCB 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF
.rodata:0000000000042910 01 00 00 00 02 00 00 00 04 00+xmmword_42910 DCB 1, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0