当前位置:网站首页>Frida hook so layer, protobuf data analysis
Frida hook so layer, protobuf data analysis
2022-07-06 16:00:00 【Catch the king before the thief】
Mobile phone installed app , Setting agent , Then start capturing bags .
It is found that the data cannot be decrypted , View requested url yes http://lbs.jt.sh.cn:8082/app/rls/monitor, Use jadx Decompile app Search this after url( Tips : You can just search url Part of , Because of the request url It may be made of several parts ), Search here rls/monitor,
Click in , And then in Right click ---> Find the case
Click in again
127 Line is add to post data, And the above packet capturing results can correspond to , So this part of the code is the code that needs to be analyzed .
see com.shjt.map.data.rline.Response, You can see Protoc.Response response = Protoc.Response.parseFrom(Native.decode2(bytes));
In the view decode2 function , As you can see, yes native Function of type , Is in so In the library
decompression apk file , find so The library files libnative.so , Use ida pro open , And then the search java_ Initial function
Click in , Then press F5 Check the pseudo code :
protobuf Grammar Chinese Translation :https://colobu.com/2017/03/16/Protobuf3-language-guide/
Protobuf Forward flow
Protobuf Advanced —— Use Python operation Protobuf:https://blog.csdn.net/a464057216/article/details/54932719
proto.exe Compile command , Automatic generation python Program :protoc --python_out=. addressbook.proto
compile addressbook.proto file , Generate addressbook_pb2.py
utilize proto.exe Inverse solution data protoc.exe --decode_raw < D:\a.bin
protoc Order help :
protoc -help
Usage: protoc [OPTION] PROTO_FILES
Parse PROTO_FILES and generate output based on the options given:
-IPATH, --proto_path=PATH Specify the directory in which to search for
imports. May be specified multiple times;
directories will be searched in order. If not
given, the current working directory is used.
If not found in any of the these directories,
the --descriptor_set_in descriptors will be
checked for required proto file.
--version Display version number
-h, --help Help information
--encode=MESSAGE_TYPE Read text format information from standard input , Then output binary data from standard output ,
You need to specify the PROTO_FILES
--deterministic_output When using --encode, ensure map fields are
deterministically ordered. Note that this order
is not canonical, and changes across builds or
releases of protoc.
--decode=MESSAGE_TYPE Read... From standard input 2 Hexadecimal data , Then output as text to standard output ,
You need to specify the PROTO_FILES
--decode_raw Read any... From standard input protocol data , And then to tag/value Format output to standard output ,
You don't have to specify PROTO_FILES
--descriptor_set_in=FILES Specifies a delimited list of FILES
each containing a FileDescriptorSet (a
protocol buffer defined in descriptor.proto).
The FileDescriptor for each of the PROTO_FILES
provided will be loaded from these
FileDescriptorSets. If a FileDescriptor
appears multiple times, the first occurrence
will be used.
-oFILE, Writes a FileDescriptorSet (a protocol buffer,
--descriptor_set_out=FILE defined in descriptor.proto) containing all of
the input files to FILE.
--include_imports When using --descriptor_set_out, also include
all dependencies of the input files in the
set, so that the set is self-contained.
--include_source_info When using --descriptor_set_out, do not strip
SourceCodeInfo from the FileDescriptorProto.
This results in vastly larger descriptors that
include information about the original
location of each decl in the source file as
well as surrounding comments.
--dependency_out=FILE Write a dependency output file in the format
expected by make. This writes the transitive
set of input file paths to FILE
--error_format=FORMAT Set the format in which to print errors.
FORMAT may be 'gcc' (the default) or 'msvs'
(Microsoft Visual Studio format).
--fatal_warnings Make warnings be fatal (similar to -Werr in
gcc). This flag will make protoc return
with a non-zero exit code if any warnings
are generated.
--print_free_field_numbers Print the free field numbers of the messages
defined in the given proto files. Groups share
the same field number space with the parent
message. Extension ranges are counted as
occupied fields numbers.
--plugin=EXECUTABLE Specifies a plugin executable to use.
Normally, protoc searches the PATH for
plugins, but you may specify additional
executables not in the path using this flag.
Additionally, EXECUTABLE may be of the form
NAME=PATH, in which case the given plugin name
is mapped to the given executable even if
the executable's own name differs.
--cpp_out=OUT_DIR Generate C++ header and source.
--csharp_out=OUT_DIR Generate C# source file.
--java_out=OUT_DIR Generate Java source file.
--js_out=OUT_DIR Generate JavaScript source.
--kotlin_out=OUT_DIR Generate Kotlin file.
--objc_out=OUT_DIR Generate Objective-C header and source.
--php_out=OUT_DIR Generate PHP source file.
--python_out=OUT_DIR Generate Python source file.
--ruby_out=OUT_DIR Generate Ruby source file.
@<filename> Read options and filenames from file. If a
relative file path is specified, the file
will be searched in the working directory.
The --proto_path option will not affect how
this argument file is searched. Content of
the file will be expanded in the position of
@<filename> as in the argument list. Note
that shell expansion is not applied to the
content of the file (i.e., you cannot use
quotes, wildcards, escapes, commands, etc.).
Each line corresponds to a single argument,
even if it contains spaces.
Be careful :window Termimal It can only be carried out cmd command , Can't execute linux command ,cmder ( https://cmder.net/ ) That is, it can be executed cmd command , It can also be executed linux Some orders of , install cmder Then perform the inverse solution of the data
Example protobuf binary data :https://api.bilibili.com/x/v2/dm/web/seg.so?type=1&oid=168855206&pid=98919207&segment_index=1
Click to download one seg.so The file of , Then execute the reverse solution command :protoc.exe --decode_raw < "seg.so"
Be careful : Because no proto file , So after decomposing the data , The value is right , But no key,
Inverse solution Protobuf Method
Method 1 : Restore .proto file :
- 1. utilize protoc.exe Anti parsing protobuf data
- 2. According to the anti parsed data , Revert out .proto file
- 3. use protoc.exe compile .proto file , Generate py Program
- 4. use py Programs can easily serialize and deserialize
Method 2 : utilize blackboxprotobuf Library direct operation protobuf data , No need to restore .proto file
# -*- coding: utf-8 -*-
# @Author : The Buddha bless , never bug
# @Date :
# @File : temp.py
# @Software: PyCharm
# @description : XXX
import blackboxprotobuf
def main():
seg_so = None
with open('d:/seg.so', 'rb') as f:
seg_so = f.read()
msg, typ = blackboxprotobuf.protobuf_to_json(seg_so, message_type=None)
print(msg)
print(typ)
if __name__ == '__main__':
main()
pass
Encryption and decryption related knowledge :
hook Encryption class :
Usage of various encryption classes ,key iv Plaintext How to obtain ciphertext , Again hook Corresponding classes and methods
AES https://www.cnblogs.com/widgetbox/p/11611201.html
RSA https://blog.csdn.net/qq_22075041/article/details/80698665
DES https://www.jianshu.com/p/bf6b4afaf41e
MD5 SHA Equal digest algorithm https://blog.csdn.net/baidu_34045013/article/details/80687557
HMAC Abstract algorithm https://blog.csdn.net/cdzwm/article/details/6973345
android Of rsa The encryption filling method is RSA when , yes NoPadind RSA/ECB/NoPadding,
And the standard jdk Fill in yes RSA when , Refer to PKCS1 fill ,RSA/ECB/PKCS1Padding, it is to be noted that
RSA Encryption popular science https://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html
RSA Encryption popular science https://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html
RSA Key length relationship https://cloud.tencent.com/developer/article/1199963
python rsa Encryption library https://pycryptodome.readthedocs.io/en/latest/src/examples.html#generate-an-rsa-key
Public private key ASN.1 structure https://blog.csdn.net/wzj_whut/article/details/86477568
ASN.1、PKCS、PEM The relationship between https://blog.csdn.net/qq_39385118/article/details/107510032
AES encryption : A symmetric encryption , Encryption and decryption require : The key (key),iv, Encryption mode Three parameters , When encrypting, the plaintext needs to be aligned first ,kv and iv There are length regulations (AES-128、AES-192 and AES-256), The length of plaintext should be 16 Multiple , Otherwise, add 0 Make up the length .
You can see
- function j_aes_key_setup Used for construction aes
- function j_aes_encrypt_cbc To decrypt
So we need to hook These two functions
First analysis j_aes_key_setup This function , Keep chasing in , And then find export The function name of ,
You can see that the function name is _Z13aes_key_setupPKhPji,hook When you need hook This function name , In the same way, we can find j_aes_encrypt_cbc hook when export The function name of is _Z15aes_encrypt_cbcPKhjPhPKjiS0_
frida hook js The code is as follows :
Interceptor Usage document :https://frida.re/docs/javascript-api/#interceptor
function printstack() {
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()));
}
function hook_so() {
console.log("\r");
var Requester = Java.use('com.shjt.map.view.layout.realtime.LineLayout$Requester');
Requester.request.implementation = function (p1) {
this.request(p1)
}
var Req = Java.use('com.shjt.map.data.rline.Request');
Req.toString.implementation = function (p1) {
//send(this.mBuilder.build().toByteArray())
var tmp = this.toString()
send('11111111:' + tmp)
return tmp
}
var ByteString = Java.use('com.android.okhttp.okio.ByteString')
var Native = Java.use('com.shjt.map.tool.Native');
Native.decode2.implementation = function (pp) {
console.log("str :" + Java.use('java.lang.String').$new(pp));
// Because some of the byte arrays are also invisible when converted into strings , So turn to 16 Base number
console.log("hex :" + ByteString.of(pp).hex());
console.log("array :" + JSON.stringify(pp));
return this.decode2(pp)
}
var soBaseAddress = Module.findBaseAddress("libnative.so");
if (soBaseAddress) {
// lookup aes_key_setup function
var aes_key_setup = Module.findExportByName("libnative.so", '_Z13aes_key_setupPKhPji');
if (aes_key_setup) {
console.log(" find aes_key_setup")
Interceptor.attach(aes_key_setup, {
onEnter: function (args) {
// console.log("aes_key_setup args type " + typeof args);
// console.log("aes_key_setup args[0] " + typeof args[0].readByteArray(16) + " " + args[0].readByteArray(16));
console.log("aes_key_setup args[0] ", args[0].readByteArray(16));
console.log("aes_key_setup args[1] ", args[1].readByteArray(16));
console.log("aes_key_setup args[2] ", args[2].toInt32());
},
onLeave: function (retval) {
console.log("aes_key_setup Return value :" + retval);
}
})
} else {
console.log(" Did not find aes_key_setup")
}
// lookup aes_encrypt_cbc function
var aes_encrypt_cbc = Module.findExportByName("libnative.so", '_Z15aes_encrypt_cbcPKhjPhPKjiS0_');
if (aes_encrypt_cbc) {
console.log(" find aes_encrypt_cbc")
Interceptor.attach(aes_encrypt_cbc, {
onEnter: function (args) {
// console.log("aes_encrypt_cbc args type " + typeof args);
// console.log("aes_encrypt_cbc args[0] " + typeof args[0].readByteArray(16) + " " + args[0].readByteArray(16));
console.log("aes_encrypt_cbc args[0] ", args[0].readByteArray(16));
console.log("aes_encrypt_cbc args[1] ", args[1].toInt32());
console.log("aes_encrypt_cbc args[2] ", args[2].readByteArray(16));
console.log("aes_encrypt_cbc args[3] ", args[3].readByteArray(16));
console.log("aes_encrypt_cbc args[4] ", args[4].toInt32());
console.log("aes_encrypt_cbc args[5] ", args[5].readByteArray(16));
},
onLeave: function (retval) {
console.log("aes_encrypt_cbc Return value :" + retval);
}
})
} else {
console.log(" Did not find aes_encrypt_cbc")
}
}
}
function main() {
Java.perform(hook_so);
}
setImmediate(main);
j_aes_key_setup((const unsigned __int8 *)v18, (unsigned int *)v15, 128) The function has three arguments
- The first parameter and The second parameter is a pointer ,
- The third parameter It's a int Integers
j_aes_encrypt_cbc((const unsigned __int8 *)p, v11, v12, (const unsigned int *)v15, 128, (const unsigned __int8 *)v17); Function has 6 Parameters
- The first parameter : Pointer types
- The second parameter :signed int type , It's an integer.
- The third parameter : Pointer types
- Fourth parameter : Pointer types
- Fifth parameter :int type , It's an integer.
- Sixth parameter : Pointer types
frida About pointer operation :https://frida.re/docs/javascript-api/#nativepointer
frida js Why is the middle pointer used readByteArray To deal with it ???
because AES When final processing , It's all converted into " Byte array " To deal with , So use readByteArray To deal with it
Why read 16 byte ???
because AES The length is specified ( 128、192、256 ), You can see j_aes_key_setup and j_aes_encrypt_cbc Function parameters have 128,128bit / 8 = 16Byte, All temporarily can be assumed to be read 16 byte .
Or use ida pro Dynamic debugging so , Determine the value of the parameter , This belongs to another technical category and is not expanded ...
start-up frida-server
see apk Package name
function js The script goes on hook. Carry out orders :frida -U -F com.xxx.map -l .\hook_so.js --no-pause
You can see j_aes_key_setup((const unsigned __int8 *)v18, (unsigned int *)v15, 128) The function has three arguments
- v18 The data stored in it is 2f d3 02 8e 14 a4 5d 1f 8b 6e b0 b2 ad b7 ca af
- v15 The data stored in it is 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- The third parameter yes 128
j_aes_encrypt_cbc((const unsigned __int8 *)p, v11, v12, (const unsigned int *)v15, 128, (const unsigned __int8 *)v17); Function has 6 Parameters
- p Parameter values 0a 27 0a 18 2f 70 72 6f 74 6f 63 2e 52 65 71 75 key value
- v11 Parameter values 48
- v12 Parameter values 00 00 00 00 20 00 00 00 61 62 6c 65 2d 61 6e 79
- v15 Parameter values 8e 02 d3 2f 1f 5d a4 14 b2 b0 6e 8b af ca b7 ad
- 128
- v17 75 4c 8f d5 84 fa cf 62 10 37 6b 2b 72 b0 63 e4 iv value
decode2 Parametric 16 Hexadecimal data :
Now? key、iv、16 There are all hexadecimal data , You can try to decrypt :
Python Of AES encryption and decryption :https://www.cnblogs.com/niuu/p/10107212.html
AES There are five encryption methods :ECB, CBC, CTR, CFB, OFB
From the perspective of security CBC Encryption method , Here is CBC、ECB Of two encryption methods python Realization
python stay Windows Next use AES When installing pycryptodome modular pip install pycryptodome
# Import the required package first
pip3 install Crypto
# Install again pycrypto
pip3 install pycrypto
from Crypto.Cipher import AES # It was successfulpython stay Linux Next use AES When installing pycrypto modular pip install pycrypto
- CBC Encryption requires a 16 bit key ( secret key ) and A sixteen iv( Offset )
- ECB Encryption is not required iv
AES CBC Encrypted python Realization
from Crypto.Cipher import AES
from binascii import b2a_hex, a2b_hex
# If text Insufficient 16 The multiples of bits are filled with spaces as 16 position
def add_to_16(text):
if len(text.encode('utf-8')) % 16:
add = 16 - (len(text.encode('utf-8')) % 16)
else:
add = 0
text = text + ('\0' * add)
return text.encode('utf-8')
# Encryption function
def encrypt(text):
key = '9999999999999999'.encode('utf-8')
mode = AES.MODE_CBC
iv = b'qqqqqqqqqqqqqqqq'
text = add_to_16(text)
cryptos = AES.new(key, mode, iv)
cipher_text = cryptos.encrypt(text)
# because AES The encrypted string is not necessarily ascii Of the character set , There may be a problem saving the output , So it turns to 16 Base string
return b2a_hex(cipher_text)
# After decryption , Remove the complementary spaces and use strip() Get rid of
def decrypt(text):
key = '9999999999999999'.encode('utf-8')
iv = b'qqqqqqqqqqqqqqqq'
mode = AES.MODE_CBC
cryptos = AES.new(key, mode, iv)
plain_text = cryptos.decrypt(a2b_hex(text))
return bytes.decode(plain_text).rstrip('\0')
if __name__ == '__main__':
e = encrypt("hello world") # encryption
d = decrypt(e) # Decrypt
print(" encryption :", e)
print(" Decrypt :", d)
AES ECB Encrypted python Realization
"""
ECB There is no offset
"""
from Crypto.Cipher import AES
from binascii import b2a_hex, a2b_hex
def add_to_16(text):
if len(text.encode('utf-8')) % 16:
add = 16 - (len(text.encode('utf-8')) % 16)
else:
add = 0
text = text + ('\0' * add)
return text.encode('utf-8')
# Encryption function
def encrypt(text):
key = '9999999999999999'.encode('utf-8')
mode = AES.MODE_ECB
text = add_to_16(text)
cryptos = AES.new(key, mode)
cipher_text = cryptos.encrypt(text)
return b2a_hex(cipher_text)
# After decryption , Remove the complementary spaces and use strip() Get rid of
def decrypt(text):
key = '9999999999999999'.encode('utf-8')
mode = AES.MODE_ECB
cryptor = AES.new(key, mode)
plain_text = cryptor.decrypt(a2b_hex(text))
return bytes.decode(plain_text).rstrip('\0')
if __name__ == '__main__':
e = encrypt("hello world") # encryption
d = decrypt(e) # Decrypt
print(" encryption :", e)
print(" Decrypt :", d)
test :
# -*- coding: utf-8 -*-
# @Author : The Buddha bless , never bug
# @Date :
# @File : temp.py
# @Software: PyCharm
# @description : XXX
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import binascii
def main():
# with open('D:\monitor.bin', 'rb') as f:
# c = f.read()
key = '2fd3028e14a45d1f8b6eb0b2adb7caaf'
iv = '754c8fd584facf6210376b2b72b063e4'
aes = AES.new(binascii.a2b_hex(key), AES.MODE_CBC, binascii.a2b_hex(iv))
hex_str = '8509209294464b3e84a122800c9419068fa44cb5827e4df3db42212a6054243a55793243b8d6479773d67ab74749611d987ab38c274bf716a2c66a8f233e9683667af7e84119d371b9926abc6f8294b266534ddb25f8ef015a16c60b770d3198'
plaintext = aes.decrypt(binascii.a2b_hex(hex_str))
print(plaintext)
if __name__ == '__main__':
main()
pass
Top up key、iv、hex Replace next , And then run , There is no error in the procedure , explain Correct transfer parameters .
The following is to write code , request URL obtain respone data , Then decrypt the data to get protobuf Binary data in format , To resolve protobuf Count ... Slightly slightly slightly
边栏推荐
- Flink 使用之 CEP
- Opencv learning log 12 binarization of Otsu method
- VS2019初步使用
- 基于web的照片数码冲印网站
- Cost accounting [19]
- Penetration testing (5) -- a collection of practical skills of scanning King nmap and penetration testing tools
- 程序员的你,有哪些炫技的代码写法?
- [exercise-1] (UVA 673) parentheses balance/ balanced brackets (stack)
- Matlab comprehensive exercise: application in signal and system
- Nodejs+vue online fresh flower shop sales information system express+mysql
猜你喜欢
PySide6 信号、槽
渗透测试 2 --- XSS、CSRF、文件上传、文件包含、反序列化漏洞
mysql导入数据库报错 [Err] 1273 – Unknown collation: ‘utf8mb4_0900_ai_ci’
渗透测试 ( 8 ) --- Burp Suite Pro 官方文档
入门C语言基础问答
Information security - Epic vulnerability log4j vulnerability mechanism and preventive measures
Ball Dropping
基于web的照片数码冲印网站
渗透测试 ( 3 ) --- Metasploit Framework ( MSF )
Matlab comprehensive exercise: application in signal and system
随机推荐
Perform general operations on iptables
[exercise-6] (PTA) divide and conquer
Accounting regulations and professional ethics [1]
TCP的三次握手与四次挥手
Research Report of peripheral venous catheter (pivc) industry - market status analysis and development prospect prediction
用C语言写网页游戏
Penetration test (8) -- official document of burp Suite Pro
[exercise-3] (UVA 442) matrix chain multiplication
nodejs爬虫
【练习-2】(Uva 712) S-Trees (S树)
[teacher Gao UML software modeling foundation] collection of exercises and answers for level 20 cloud class
Information security - Epic vulnerability log4j vulnerability mechanism and preventive measures
渗透测试 ( 8 ) --- Burp Suite Pro 官方文档
Cost accounting [18]
Cost accounting [19]
动态规划前路径问题优化方式
信息安全-威胁检测-flink广播流BroadcastState双流合并应用在过滤安全日志
[exercise-9] Zombie's Treasury test
Opencv learning log 16 paperclip count
Research Report on surgical fluid treatment industry - market status analysis and development prospect prediction