白乐天

道阻且长,行则将至。

海南航空app绕过强制更新及hnairSign参数逆向

App信息

包名:com.rytong.hnair

绕过强制更新

启动app,出现如下弹窗,强制更新

jadx反编译找到更新弹窗位置

搜索字符串立刻更新

双击跳转

这个字符串对应的资源名字是version_update__dialog_btn_confirm_text,搜索它,找到它被调用的位置

双击跳转

如下,在这里设置文本内容

通过查找用例跳转到上层方法

这里面的show()方法就是显示弹窗的,把它hook掉,弹窗就不会显示了

hook show()

1
2
3
4
5
6
7
8
9
10
11
function hook_show() {
Java.perform(function() {
let k = Java.use("f6.k");
k["show"].implementation = function () {
console.log(`k.show is called`);
// this["show"]();
};
});
}

hook_show();

如下,弹窗就被hook掉了

抓包分析数据

点击登录,在通过了验证码校验之后会发送请求,及获取响应,抓取数据包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
url=https://app.hnair.com/appum/common/auth/login?hnairSign=0564996701D210B22DBB6F7AAC619C260CA0A114

params={
"hnairSign": "0564996701D210B22DBB6F7AAC619C260CA0A114"
}
headers = {
"appver": "9.0.0",
"content-type": "application/json; charset=UTF-8",
"content-length": "978",
"accept-encoding": "gzip",
"user-agent": "okhttp/4.9.1"
}

json = {
"common": {
"akey": "184C5F04D8BE43DCBD2EE3ABC928F616",
"aname": "com.rytong.hnair",
"atarget": "standard",
"aver": "9.0.0",
"did": "e574bb78db98d8ea",
"dname": "Google_Pixel 3",
"gtcid": "514490d4a99745bba1ee3c67a21e5db6",
"mchannel": "huawei",
"schannel": "AD",
"slang": "zh-CN",
"sname": "google/blueline/blueline:10/QQ3A.200705.002/6506677:user/release-keys",
"stime": "1740304439444",
"sver": "10",
"system": "AD",
"szone": "+0800",
"abuild": "64249",
"riskToken": "67baf03fNy5MXBFAoCK6UHULA4nAOmGMshN11oP3",
"captchaToken": "3037239175221CB99962A4E18B9A763B241E38AEC7657CDE570574B886AB788BAD219143A251B3B95BBEB9D693FDDAEED872D366E0D38095458726DDE75C845A49F1A6FEB95AE56461B44FC60507BA52:67baf0340PiDJ4ft549lxVciFEip4Qz9R8ox1Ra3",
"hver": "9.0.0.35417.7ac793f2e.standard"
},
"data": {
"number": "18888888888",
"pin": "dNKwvxEU5MhDIHvDxC9W3GkIe9Y9Qgn1GFIL2Rnl3lpIYOAUAHyAfcX7SuEvKD3fZUNb8oHkG2fh\njMktd9dIib489LGK65FO41So6mzRK+mn/5wqHzqSj0oFiBTJOkQ8aPpUNLoNKCLClYrS5q4aWhB6\n7ncdYn8dobgaz7TyjVk=\n",
"toSave": true
}
}

要逆向分析的是hnairSign

参数分析

jadx反编译

搜索hnairSign

hnairSign

在如下位置出现

hnairSign它的值是从signRequest()方法返回的

signRequest()

这里面从参数aVar里面获取headersForSignqueryForSignrequestBodyForSign及生成参数stra10,然后把这些参数传递个getHNASignature()方法,其返回值再传递给i.p()方法,i.p()的返回值应该是一个数组类型,然后获取第一个元素。

getHNASignature()

这是一个native方法,返回值是一个字符串

i.p()

这里面是根据字节数组strArrcharSequence返回一个List,最后通过get(0)获取第0个元素作为signRequest()方法的返回值。

hook getHNASignature()

1
2
3
4
5
6
7
8
9
10
11
12
13
function getHNASignature() {
Java.perform(function() {
let HNASignature = Java.use("com.rytong.hnair.HNASignature");
HNASignature["getHNASignature"].implementation = function (str, str2, str3, str4, str5) {
console.log(`HNASignature.getHNASignature is called: str=${str}, str2=${str2}, str3=${str3}, str4=${str4}, str5=${str5}`);
let result = this["getHNASignature"](str, str2, str3, str4, str5);
console.log(`HNASignature.getHNASignature result=${result}`);
return result;
};
});
}

getHNASignature();

hook结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
HNASignature.getHNASignature is called: str={}, str2={}, str3={"akey":"184C5F04D8BE43DCBD2EE3ABC928F616","aname":"com.rytong.hnair","atarget":"standard","aver":"9.0.0","did":"e574bb78db98d8ea","dname":"Google_Pixel 3","gtcid":"f442b091aa961a407165841e3a084147","mchannel":"huawei","schannel":"AD","slang":"zh-CN","sname":"google\/blueline\/blueline:10\/QQ3A.200705.002\/6506677:user\/release-keys","stime":"1740317961228","sver":"10","system":"AD","szone":"+0800","abuild":"64249","riskToken":"67bb2505kRG0Hwidai2tEm3hYFUX1xCXvH8fuOk3","hver":"9.0.0.35417.7ac793f2e.standard","number":"18888888888","pin":"awBta70HBNvOLA+L5Vlaas59trF+M0EI6lOgVDjQhG1QdoY\/6ck1GTaeZOeSXiwFpG3e8vgj6xuq\n1weM+DFKhl8aSi1Lhn7p1s51bdcPmvZ5DW3kYSV\/8DvpnYGjPKgQ79aKEBDo3AvuGZpZ+pg2rHBm\nWAgoyrVfSsBI6KLOsZg=\n","toSave":true}, str4=21047C596EAD45209346AE29F0350491, str5=F6B15ABD66F91951036C955CB25B069F
HNASignature.getHNASignature

result=E184DAEE44DFA522CE974EAB75EBC60DAAEC321D>>
64249184C5F04D8BE43DCBD2EE3ABC928F616com.rytong.hnairstandard9.0.0e574bb78db98d8eaGoogle_Pixel 3f442b091aa961a407165841e3a0841479.0.0.35417.7ac793f2e.standardhuawei18888888888awBta70HBNvOLA+L5Vlaas59trF+M0EI6lOgVDjQhG1QdoY/6ck1GTaeZOeSXiwFpG3e8vgj6xuq
1weM+DFKhl8aSi1Lhn7p1s51bdcPmvZ5DW3kYSV/8DvpnYGjPKgQ79aKEBDo3AvuGZpZ+pg2rHBm
WAgoyrVfSsBI6KLOsZg=
67bb2505kRG0Hwidai2tEm3hYFUX1xCXvH8fuOk3ADzh-CNgoogle/blueline/blueline:10/QQ3A.200705.002/6506677:user/release-keys174031796122810AD+0800true>>F6B15ABD66F91951036C955CB25B069F

result=515FCBCF21BD505882222C4898DCA5A13014C134>>
64249184C5F04D8BE43DCBD2EE3ABC928F616com.rytong.hnairstandard9.0.0e574bb78db98d8eaGoogle_Pixel 3f442b091aa961a407165841e3a0841479.0.0.35417.7ac793f2e.standardhuawei18888888888awBta70HBNvOLA+L5Vlaas59trF+M0EI6lOgVDjQhG1QdoY/6ck1GTaeZOeSXiwFpG3e8vgj6xuq
1weM+DFKhl8aSi1Lhn7p1s51bdcPmvZ5DW3kYSV/8DvpnYGjPKgQ79aKEBDo3AvuGZpZ+pg2rHBm
WAgoyrVfSsBI6KLOsZg=
67bb2505kRG0Hwidai2tEm3hYFUX1xCXvH8fuOk3ADzh-CNgoogle/blueline/blueline:10/QQ3A.200705.002/6506677:user/release-keys174031796122810AD+0800true>>F6B15ABD66F91951036C955CB25B069F

hook signRequest()

两个方法一起hook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function getHNASignature() {
Java.perform(function() {
let HNASignature = Java.use("com.rytong.hnair.HNASignature");
HNASignature["getHNASignature"].implementation = function (str, str2, str3, str4, str5) {
console.log(`HNASignature.getHNASignature is called: str=${str}, str2=${str2}, str3=${str3}, str4=${str4}, str5=${str5}`);
let result = this["getHNASignature"](str, str2, str3, str4, str5);
console.log(`HNASignature.getHNASignature result=${result}`);
return result;
};
});
}

getHNASignature();

function hook_signRequest() {
Java.perform(function() {
let ApiSignInterceptor = Java.use("com.hnair.airlines.repo.common.ApiSignInterceptor");
ApiSignInterceptor["signRequest"].implementation = function (aVar) {
console.log(`ApiSignInterceptor.signRequest is called: aVar=${aVar}`);
let result = this["signRequest"](aVar);
console.log(`ApiSignInterceptor.signRequest result=${result}`);
return result;
};
});
}

hook_signRequest();

hook结果

1
2
3
4
5
6
7
ApiSignInterceptor.signRequest is called: aVar=[object Object]
HNASignature.getHNASignature is called: str={}, str2={}, str3={"akey":"184C5F04D8BE43DCBD2EE3ABC928F616","aname":"com.rytong.hnair","atarget":"standard","aver":"9.0.0","did":"e574bb78db98d8ea","dname":"Google_Pixel 3","gtcid":"f442b091aa961a407165841e3a084147","mchannel":"huawei","schannel":"AD","slang":"zh-CN","sname":"google\/blueline\/blueline:10\/QQ3A.200705.002\/6506677:user\/release-keys","stime":"1740318611305","sver":"10","system":"AD","szone":"+0800","abuild":"64249","riskToken":"67bb278f3kk3a3WeYbgRPyoBNPlIAdRaYordBAp3","hver":"9.0.0.35417.7ac793f2e.standard","number":"18888888888","pin":"YtirjBXqjfmcYkrHpTOw46uZtkKiQTt8RvSVGkZKby025\/m2Yrv1zMUHehUz+sEDkHENCIPyJGuR\nbqKOxirFsWuMr0W4vGizxLnHOOLh9BTxzMpjpPsihKYvEMS0DlRlxp86LsEXmjviAt55e4ByhkEu\nVq5ckk5NVufdx\/u7CFI=\n","toSave":true}, str4=21047C596EAD45209346AE29F0350491, str5=F6B15ABD66F91951036C955CB25B069F
HNASignature.getHNASignature result=E2096151703457F87514E0067262519B8E6F6774>>64249184C5F04D8BE43DCBD2EE3ABC928F616com.rytong.hnairstandard9.0.0e574bb78db98d8eaGoogle_Pixel 3f442b091aa961a407165841e3a0841479.0.0.35417.7ac793f2e.standardhuawei18888888888YtirjBXqjfmcYkrHpTOw46uZtkKiQTt8RvSVGkZKby025/m2Yrv1zMUHehUz+sEDkHENCIPyJGuR
bqKOxirFsWuMr0W4vGizxLnHOOLh9BTxzMpjpPsihKYvEMS0DlRlxp86LsEXmjviAt55e4ByhkEu
Vq5ckk5NVufdx/u7CFI=
67bb278f3kk3a3WeYbgRPyoBNPlIAdRaYordBAp3ADzh-CNgoogle/blueline/blueline:10/QQ3A.200705.002/6506677:user/release-keys174031861130510AD+0800true>>F6B15ABD66F91951036C955CB25B069F
ApiSignInterceptor.signRequest result=E2096151703457F87514E0067262519B8E6F6774

可以看到signRequest最终的返回结果就是>>符号分隔前的内容

so分析

在反编译代码里并没有显式的支出加载的so,需要自行定位

定位so

动态注册hook RegisterNatives()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function hook_RegisterNatives() {
var RegisterNatives_addr = null;
var symbols = Process.findModuleByName("libart.so").enumerateSymbols();
for (var i = 0; i < symbols.length; i++) {
var symbol = symbols[i].name;
if ((symbol.indexOf("CheckJNI") == -1) && (symbol.indexOf("JNI") >= 0)) {
if (symbol.indexOf("RegisterNatives") >= 0) {
RegisterNatives_addr = symbols[i].address;
console.log("RegisterNatives_addr: ", RegisterNatives_addr);
}
}
}
Interceptor.attach(RegisterNatives_addr, {
onEnter: function (args) {
var env = args[0];
var jclass = args[1];
var class_name = Java.vm.tryGetEnv().getClassName(jclass);
var methods_ptr = ptr(args[2]);
var method_count = args[3].toInt32();
console.log("RegisterNatives method counts: ", method_count);
for (var i = 0; i < method_count; i++) {
var name = methods_ptr.add(i * Process.pointerSize * 3).readPointer().readCString();
var sig = methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize).readPointer().readCString();
var fnPtr_ptr = methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2).readPointer();
var find_module = Process.findModuleByAddress(fnPtr_ptr);
console.log("ClasssName: ", class_name, "MethodName: ", name, "Sig: ", sig, "Function_addr: ", fnPtr_ptr, "ModuleName: ", find_module.name, "Fun_Offset: ", ptr(fnPtr_ptr).sub(find_module.base));
}
},
onLeave: function (retval) {}
});
}
hook_RegisterNatives()

在输出的结果中并没有找到,说明不是动态注册的

静态注册hook dlsym()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function hook_dlsym(){
// 获取dlsym函数的地址
let dlsymAddr = Module.findExportByName("libdl.so","dlsym");
console.log(dlsymAddr);
// hook dlsym
Interceptor.attach(dlsymAddr,{
onEnter:function(args){
this.args1 = args[1];
},
onLeave:function(retval){
let md= Process.findModuleByAddress(retval);
if(md==null)return;
console.log("function:"+this.args1.readCString(),"module:"+md.name,"address:"+retval,"offset:"+retval.sub(md.base));
}
})
}

hook_dlsym()

找到so为libsignature.so

IDA反编译so

在静态函数中查找

双击跳转到函数位置,转换为伪代码

显示调用了HNASignature()然后调用了toString()

HNASignature()

查看其符号名,先对它进行hook

hook HNASignature()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function hook_HNASignature(){
let HNASignatureAddr = Module.findExportByName("libsignature.so", "_ZN12HNASignatureC2ERKSsS1_S1_S1_S1_");
Interceptor.attach(HNASignatureAddr,{
onEnter:function(args){
console.log("HNASignature is called");
console.log("HNASignature args[0]:"+args[0]);
console.log("HNASignature args[1]:"+args[1].readPointer().readCString());
console.log("HNASignature args[2]:"+args[2].readPointer().readCString());
console.log("HNASignature args[3]:"+args[3].readPointer().readCString());
console.log("HNASignature args[4]:"+args[4].readPointer().readCString());
console.log("HNASignature args[5]:"+args[5].readPointer().readCString());
console.log("HNASignature args[6]:"+args[6]);
this.args0 = args[0];
},
onLeave:function(){
console.log(`HNASignature ret this.args0:${this.args0.readPointer().readCString()}`);
}
})
}

hook 结果

1
2
3
4
5
6
7
8
9
10
11
12
HNASignature is called
HNASignature args[0]:0x75d9c78328
HNASignature args[1]:{}
HNASignature args[2]:{}
HNASignature args[3]:{"akey":"184C5F04D8BE43DCBD2EE3ABC928F616","aname":"com.rytong.hnair","atarget":"standard","aver":"9.0.0","did":"e574bb78db98d8ea","dname":"Google_Pixel 3","gtcid":"f442b091aa961a407165841e3a084147","mchannel":"huawei","schannel":"AD","slang":"zh-CN","sname":"google\/blueline\/blueline:10\/QQ3A.200705.002\/6506677:user\/release-keys","stime":"1740326030547","sver":"10","system":"AD","szone":"+0800","abuild":"64249","riskToken":"67bb4453hhA1OpIQxuFTcxvxY25l6alW4VgMhK73","hver":"9.0.0.35417.7ac793f2e.standard","number":"18888888888","pin":"qWIFqA0ywqaZqFsywFiTmniXYBKU8WpXlGexYUIhmKAXxOwf7\/z6NRN+sBj5d36igmPekmcESPWS\n4EbGmGLkCDM3al7NzoWqhucFcxl6n8gE6wfN83kqoS\/mJjQXis5WwixL\/wUpmYH8UzRaFunSsKsg\nW5WUx1CgTWdBpYUH3d0=\n","toSave":true}
HNASignature args[4]:21047C596EAD45209346AE29F0350491
HNASignature args[5]:F6B15ABD66F91951036C955CB25B069F
HNASignature args[6]:0xfefefefefefefeff
HNASignature ret this.args0:2FD7C67C2ED04DE229D9D5C6B41534975DED3324>>64249184C5F04D8BE43DCBD2EE3ABC928F616com.rytong.hnairstandard9.0.0e574bb78db98d8eaGoogle_Pixel 3f442b091aa961a407165841e3a0841479.0.0.35417.7ac793f2e.standardhuawei18888888888qWIFqA0ywqaZqFsywFiTmniXYBKU8WpXlGexYUIhmKAXxOwf7/z6NRN+sBj5d36igmPekmcESPWS
4EbGmGLkCDM3al7NzoWqhucFcxl6n8gE6wfN83kqoS/mJjQXis5WwixL/wUpmYH8UzRaFunSsKsg
W5WUx1CgTWdBpYUH3d0=
67bb4453hhA1OpIQxuFTcxvxY25l6alW4VgMhK73ADzh-CNgoogle/blueline/blueline:10/QQ3A.200705.002/6506677:user/release-keys174032603054710AD+0800true>>F6B15ABD66F91951036C955CB25B069F

HNASignature()函数里出现了一堆函数

有尝试hook这些函数,信息量较大,后面有时间了分析,这里直接unidbg模拟执行了

unidbg模拟执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class HNASignature extends AbstractJni {
private final AndroidEmulator emulator;
private final Memory memory;
private final VM vm;

public HNASignature() {
emulator = AndroidEmulatorBuilder
.for64Bit() // for32Bit()
.build();
memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
vm = emulator.createDalvikVM(new File("unidbg-android/src/test/java/com/rytong/hnair/Hainan Airlines_9.0.0.apk"));
vm.setJni(this);
vm.setVerbose(true);
vm.loadLibrary("signature",true);
}

public String getHNASignature(){
DvmClass HNASignatureClass = vm.resolveClass("com.rytong.hnair.HNASignature");
DvmClass stringClass = vm.resolveClass("java.lang.String");
StringObject stringObject = HNASignatureClass.callStaticJniMethodObject(emulator,
"getHNASignature(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
stringClass.newObject("{}").getValue(),
stringClass.newObject("{}").getValue(),
stringClass.newObject("{\"akey\":\"184C5F04D8BE43DCBD2EE3ABC928F616\",\"aname\":\"com.rytong.hnair\",\"atarget\":\"standard\",\"aver\":\"9.0.0\",\"did\":\"e574bb78db98d8ea\",\"dname\":\"Google_Pixel 3\",\"gtcid\":\"f442b091aa961a407165841e3a084147\",\"mchannel\":\"huawei\",\"schannel\":\"AD\",\"slang\":\"zh-CN\",\"sname\":\"google\\/blueline\\/blueline:10\\/QQ3A.200705.002\\/6506677:user\\/release-keys\",\"stime\":\"1740317961228\",\"sver\":\"10\",\"system\":\"AD\",\"szone\":\"+0800\",\"abuild\":\"64249\",\"riskToken\":\"67bb2505kRG0Hwidai2tEm3hYFUX1xCXvH8fuOk3\",\"hver\":\"9.0.0.35417.7ac793f2e.standard\",\"number\":\"18888888888\",\"pin\":\"awBta70HBNvOLA+L5Vlaas59trF+M0EI6lOgVDjQhG1QdoY\\/6ck1GTaeZOeSXiwFpG3e8vgj6xuq\\n1weM+DFKhl8aSi1Lhn7p1s51bdcPmvZ5DW3kYSV\\/8DvpnYGjPKgQ79aKEBDo3AvuGZpZ+pg2rHBm\\nWAgoyrVfSsBI6KLOsZg=\\n\",\"toSave\":true}").getValue(),
stringClass.newObject("21047C596EAD45209346AE29F0350491").getValue(),
stringClass.newObject("F6B15ABD66F91951036C955CB25B069F").getValue());
String ret = stringObject.getValue();
return ret;
}

public static void main(String[] args) {
HNASignature hnaSignature = new HNASignature();
ArrayList<String> list = new ArrayList<>(Arrays.asList(hnaSignature.getHNASignature().split(">>")));
System.out.println("getHNASignature(): "+hnaSignature.getHNASignature());
System.out.println("get(0): "+list.get(0));
}
}

结果

1
2
3
4
5
6
getHNASignature(): E184DAEE44DFA522CE974EAB75EBC60DAAEC321D>>64249184C5F04D8BE43DCBD2EE3ABC928F616com.rytong.hnairstandard9.0.0e574bb78db98d8eaGoogle_Pixel 3f442b091aa961a407165841e3a0841479.0.0.35417.7ac793f2e.standardhuawei18888888888awBta70HBNvOLA+L5Vlaas59trF+M0EI6lOgVDjQhG1QdoY/6ck1GTaeZOeSXiwFpG3e8vgj6xuq
1weM+DFKhl8aSi1Lhn7p1s51bdcPmvZ5DW3kYSV/8DvpnYGjPKgQ79aKEBDo3AvuGZpZ+pg2rHBm
WAgoyrVfSsBI6KLOsZg=
67bb2505kRG0Hwidai2tEm3hYFUX1xCXvH8fuOk3ADzh-CNgoogle/blueline/blueline:10/QQ3A.200705.002/6506677:user/release-keys174031796122810AD+0800true>>F6B15ABD66F91951036C955CB25B069F

get(0): E184DAEE44DFA522CE974EAB75EBC60DAAEC321D