Open Jaffe2718 opened 1 month ago
寄了
很蓝的呐
在逆了
在逆了
逆向大佬竟在我身边
import { Base64 } from 'js-base64'
import { addFrog, runUniqueApi } from '@solar/webview'
export const encryptRequestBody = (data: any): Promise<ArrayBuffer> => {
const dataJson = JSON.stringify(data)
return new Promise((resolve, reject) => {
runUniqueApi('dataEncrypt', {
base64: Base64.encode(dataJson),
trigger: async (status: any, data: any) => {
if (data && data.result) {
const res = base64ToUint8Array(data.result).buffer
resolve(res)
} else {
reject(Error('encrypt data fail'))
addFrog({
url: '/debug/oralPK/dataEncryptFailed',
params: {
status: status,
dataJson: dataJson
},
flushFrog: false
})
}
}
}, 'LeoSecure')
})
}
const base64ToUint8Array = (base64String: string): Uint8Array => {
const padding = '='.repeat((4 - base64String.length % 4) % 4)
const base64 = (base64String + padding)
.replace(/-/g, '+')
.replace(/_/g, '/')
const rawData = Base64.atob(base64)
const outputArray = new Uint8Array(rawData.length)
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i)
}
return outputArray
}
/**
* 解密 ResponseBody
*/
export const DecryptData = (target: any, _key: string, descriptor: PropertyDescriptor) => {
const oldMethod = descriptor.value
const newMethod = async (...args: any) => {
return oldMethod.apply(target, args).then(async (res: any) => {
// @ts-ignore
const buffer = btoa(String.fromCharCode.apply(null, new Uint8Array(res)))
return await dataDecrypt(buffer)
}).catch((err: any) => {
throw err
})
}
descriptor.value = newMethod
return descriptor
}
const dataDecrypt = (result: any) => {
return new Promise(resolve => {
runUniqueApi('dataDecrypt', {
base64: result,
trigger: (status: any, data: any) => {
const decryptedData = JSON.parse(Base64.decode(data.result))
resolve(decryptedData)
if (process.env.VUE_APP_CONFIG === 'test') {
console.log('decrypted data: ', decryptedData)
}
}
}, 'LeoSecure')
})
}
翻出来一个
不知道是不是
不知道是不是
先测试一下
在逆了
66666
在逆了
66666
Base64加密???
啊有大佬写了笔记,观摩中,但大佬好像还没写完 https://github.com/xmexg/xyks
啊有大佬写了笔记,观摩中,但大佬好像还没写完 https://github.com/xmexg/xyks
那个大佬似乎也没能全部解完
在逆了
66666
我倒是有个不用逆的思路,不清楚行不行
你看看这个PUT请求,是不是有costTime
字段,我估计单位是毫秒,但是无所谓,直接改成0即可。
如果成功了我感觉效果是做题依然是手动做题,但是用时会被强行改为0,你可以试试
在逆了
66666
我倒是有个不用逆的思路,不清楚行不行 你看看这个PUT请求,是不是有
costTime
字段,我估计单位是毫秒,但是无所谓,直接改成0即可。 如果成功了我感觉效果是做题依然是手动做题,但是用时会被强行改为0,你可以试试
没用的,这只会在本地显示,服务器上未有任何更改
在逆了
66666
我倒是有个不用逆的思路,不清楚行不行 你看看这个PUT请求,是不是有
costTime
字段,我估计单位是毫秒,但是无所谓,直接改成0即可。 如果成功了我感觉效果是做题依然是手动做题,但是用时会被强行改为0,你可以试试
找不到接口感觉没用啊
@ZeroQing89 感觉本地也够装一波了,但是目前不清楚怎么改
@ZeroQing89 感觉本地也够装一波了,但是目前不清楚怎么改
本地就没意思了,这和F12调试侠有什么两样啊😂😂😂
现在最大的问题就是逆向成本有点高
很难的啦
import { Base64 } from 'js-base64' import { addFrog, runUniqueApi } from '@solar/webview' export const encryptRequestBody = (data: any): Promise<ArrayBuffer> => { const dataJson = JSON.stringify(data) return new Promise((resolve, reject) => { runUniqueApi('dataEncrypt', { base64: Base64.encode(dataJson), trigger: async (status: any, data: any) => { if (data && data.result) { const res = base64ToUint8Array(data.result).buffer resolve(res) } else { reject(Error('encrypt data fail')) addFrog({ url: '/debug/oralPK/dataEncryptFailed', params: { status: status, dataJson: dataJson }, flushFrog: false }) } } }, 'LeoSecure') }) } const base64ToUint8Array = (base64String: string): Uint8Array => { const padding = '='.repeat((4 - base64String.length % 4) % 4) const base64 = (base64String + padding) .replace(/-/g, '+') .replace(/_/g, '/') const rawData = Base64.atob(base64) const outputArray = new Uint8Array(rawData.length) for (let i = 0; i < rawData.length; ++i) { outputArray[i] = rawData.charCodeAt(i) } return outputArray } /** * 解密 ResponseBody */ export const DecryptData = (target: any, _key: string, descriptor: PropertyDescriptor) => { const oldMethod = descriptor.value const newMethod = async (...args: any) => { return oldMethod.apply(target, args).then(async (res: any) => { // @ts-ignore const buffer = btoa(String.fromCharCode.apply(null, new Uint8Array(res))) return await dataDecrypt(buffer) }).catch((err: any) => { throw err }) } descriptor.value = newMethod return descriptor } const dataDecrypt = (result: any) => { return new Promise(resolve => { runUniqueApi('dataDecrypt', { base64: result, trigger: (status: any, data: any) => { const decryptedData = JSON.parse(Base64.decode(data.result)) resolve(decryptedData) if (process.env.VUE_APP_CONFIG === 'test') { console.log('decrypted data: ', decryptedData) } } }, 'LeoSecure') }) }
res = flow.response.content
# 将字节数组转换为 Base64 编码的字符串
base64_encoded = base64.b64encode(res).decode('utf-8')
print(base64_encoded)
输出:y7KptyngpGugGocxaq6J8I5IOevoCyCJyfSyBRCvsA9fVeNKQdBN1FMynCeJODBM4fxyyvxunnezVxMQZV1oJhy9RBBg/j5bZjibxUFzdU0VwIuvZJLOQTrnyiHkQTiFMN1RBE3GhiNrsaJvYCjjC0h...
最终跟到webpack://leo-web-oral-pk/node_modules/@solar/solar-web-bridge/lib/native.js messageHandler[name]=dataDecrypt,传入encodeParam参数后跟不动了
encodeParam参数格式:base64
{"arguments":[{"base64":"y7KptyngpGugGocxaq6J8I5IOevoCyCJyfSyBRCvsA9fVeNKQdBN1FMynCeJODBM4fxyyvxunnezVxMQZV1oJhy9RBBg/j5bZjibxUFzdU0VwIuvZJLOQTrnyiHkQTiFMN1RBE3GhiNrsaJvYCjjC0h...","trigger":"dataDecrypt_1728641257271_14"}],"callback":"dataDecrypt_callback_1728641257271_15"}
这里处理完会返回一个base64的数据,最终解码得到明文 eyJwa0lkU3RyIjoiNjA5NDM0NDc...
补充:解密函数dataDecrypt似乎在LeoSecureWebViewApi.java里,不太懂java
抖音上已经有人研究出跳过答题了
import { Base64 } from 'js-base64' import { addFrog, runUniqueApi } from '@solar/webview' export const encryptRequestBody = (data: any): Promise<ArrayBuffer> => { const dataJson = JSON.stringify(data) return new Promise((resolve, reject) => { runUniqueApi('dataEncrypt', { base64: Base64.encode(dataJson), trigger: async (status: any, data: any) => { if (data && data.result) { const res = base64ToUint8Array(data.result).buffer resolve(res) } else { reject(Error('encrypt data fail')) addFrog({ url: '/debug/oralPK/dataEncryptFailed', params: { status: status, dataJson: dataJson }, flushFrog: false }) } } }, 'LeoSecure') }) } const base64ToUint8Array = (base64String: string): Uint8Array => { const padding = '='.repeat((4 - base64String.length % 4) % 4) const base64 = (base64String + padding) .replace(/-/g, '+') .replace(/_/g, '/') const rawData = Base64.atob(base64) const outputArray = new Uint8Array(rawData.length) for (let i = 0; i < rawData.length; ++i) { outputArray[i] = rawData.charCodeAt(i) } return outputArray } /** * 解密 ResponseBody */ export const DecryptData = (target: any, _key: string, descriptor: PropertyDescriptor) => { const oldMethod = descriptor.value const newMethod = async (...args: any) => { return oldMethod.apply(target, args).then(async (res: any) => { // @ts-ignore const buffer = btoa(String.fromCharCode.apply(null, new Uint8Array(res))) return await dataDecrypt(buffer) }).catch((err: any) => { throw err }) } descriptor.value = newMethod return descriptor } const dataDecrypt = (result: any) => { return new Promise(resolve => { runUniqueApi('dataDecrypt', { base64: result, trigger: (status: any, data: any) => { const decryptedData = JSON.parse(Base64.decode(data.result)) resolve(decryptedData) if (process.env.VUE_APP_CONFIG === 'test') { console.log('decrypted data: ', decryptedData) } } }, 'LeoSecure') }) }
res = flow.response.content # 将字节数组转换为 Base64 编码的字符串 base64_encoded = base64.b64encode(res).decode('utf-8') print(base64_encoded)
输出:y7KptyngpGugGocxaq6J8I5IOevoCyCJyfSyBRCvsA9fVeNKQdBN1FMynCeJODBM4fxyyvxunnezVxMQZV1oJhy9RBBg/j5bZjibxUFzdU0VwIuvZJLOQTrnyiHkQTiFMN1RBE3GhiNrsaJvYCjjC0h...
最终跟到webpack://leo-web-oral-pk/node_modules/@solar/solar-web-bridge/lib/native.js messageHandler[name]=dataDecrypt,传入encodeParam参数后跟不动了
encodeParam参数格式:base64
{"arguments":[{"base64":"y7KptyngpGugGocxaq6J8I5IOevoCyCJyfSyBRCvsA9fVeNKQdBN1FMynCeJODBM4fxyyvxunnezVxMQZV1oJhy9RBBg/j5bZjibxUFzdU0VwIuvZJLOQTrnyiHkQTiFMN1RBE3GhiNrsaJvYCjjC0h...","trigger":"dataDecrypt_1728641257271_14"}],"callback":"dataDecrypt_callback_1728641257271_15"}
这里处理完会返回一个base64的数据,最终解码得到明文 eyJwa0lkU3RyIjoiNjA5NDM0NDc...
补充:解密函数dataDecrypt似乎在LeoSecureWebViewApi.java里,不太懂java
在这呢com.fenbi.android.leo.webapp.secure.commands.DataDecryptCommand$execute$1$decryptData$1.invokeSuspend
import { Base64 } from 'js-base64' import { addFrog, runUniqueApi } from '@solar/webview' export const encryptRequestBody = (data: any): Promise<ArrayBuffer> => { const dataJson = JSON.stringify(data) return new Promise((resolve, reject) => { runUniqueApi('dataEncrypt', { base64: Base64.encode(dataJson), trigger: async (status: any, data: any) => { if (data && data.result) { const res = base64ToUint8Array(data.result).buffer resolve(res) } else { reject(Error('encrypt data fail')) addFrog({ url: '/debug/oralPK/dataEncryptFailed', params: { status: status, dataJson: dataJson }, flushFrog: false }) } } }, 'LeoSecure') }) } const base64ToUint8Array = (base64String: string): Uint8Array => { const padding = '='.repeat((4 - base64String.length % 4) % 4) const base64 = (base64String + padding) .replace(/-/g, '+') .replace(/_/g, '/') const rawData = Base64.atob(base64) const outputArray = new Uint8Array(rawData.length) for (let i = 0; i < rawData.length; ++i) { outputArray[i] = rawData.charCodeAt(i) } return outputArray } /** * 解密 ResponseBody */ export const DecryptData = (target: any, _key: string, descriptor: PropertyDescriptor) => { const oldMethod = descriptor.value const newMethod = async (...args: any) => { return oldMethod.apply(target, args).then(async (res: any) => { // @ts-ignore const buffer = btoa(String.fromCharCode.apply(null, new Uint8Array(res))) return await dataDecrypt(buffer) }).catch((err: any) => { throw err }) } descriptor.value = newMethod return descriptor } const dataDecrypt = (result: any) => { return new Promise(resolve => { runUniqueApi('dataDecrypt', { base64: result, trigger: (status: any, data: any) => { const decryptedData = JSON.parse(Base64.decode(data.result)) resolve(decryptedData) if (process.env.VUE_APP_CONFIG === 'test') { console.log('decrypted data: ', decryptedData) } } }, 'LeoSecure') }) }
res = flow.response.content # 将字节数组转换为 Base64 编码的字符串 base64_encoded = base64.b64encode(res).decode('utf-8') print(base64_encoded)
输出:y7KptyngpGugGocxaq6J8I5IOevoCyCJyfSyBRCvsA9fVeNKQdBN1FMynCeJODBM4fxyyvxunnezVxMQZV1oJhy9RBBg/j5bZjibxUFzdU0VwIuvZJLOQTrnyiHkQTiFMN1RBE3GhiNrsaJvYCjjC0h... 最终跟到webpack://leo-web-oral-pk/node_modules/@solar/solar-web-bridge/lib/native.js messageHandler[name]=dataDecrypt,传入encodeParam参数后跟不动了 encodeParam参数格式:base64
{"arguments":[{"base64":"y7KptyngpGugGocxaq6J8I5IOevoCyCJyfSyBRCvsA9fVeNKQdBN1FMynCeJODBM4fxyyvxunnezVxMQZV1oJhy9RBBg/j5bZjibxUFzdU0VwIuvZJLOQTrnyiHkQTiFMN1RBE3GhiNrsaJvYCjjC0h...","trigger":"dataDecrypt_1728641257271_14"}],"callback":"dataDecrypt_callback_1728641257271_15"}
这里处理完会返回一个base64的数据,最终解码得到明文 eyJwa0lkU3RyIjoiNjA5NDM0NDc... 补充:解密函数dataDecrypt似乎在LeoSecureWebViewApi.java里,不太懂java在这呢com.fenbi.android.leo.webapp.secure.commands.DataDecryptCommand$execute$1$decryptData$1.invokeSuspend
最终看调用了com.fenbi.android.leo.imgsearch.sdk.utils.e.c System.loadLibrary("ContentEncoder")加载了本地库更不知道咋整了,师傅有思路嘛
各位大佬逆向完了能不能封装成python库呀,这样方便偷懒,要是真的有这个库就舒服了 类似这样:
pip install xiaoyuan_toolkit
import xiaoyuan_tookit
# 抓包...
packet_json: dict = xiaoyuan_tookit.decrypt(b'被加密的内容')
# 解析并改包...
enc = xiaoyuan_toolkit.encrypt(packet_json) # 重新加密
# 再把改后的包给客户端
import { Base64 } from 'js-base64' import { addFrog, runUniqueApi } from '@solar/webview' export const encryptRequestBody = (data: any): Promise<ArrayBuffer> => { const dataJson = JSON.stringify(data) return new Promise((resolve, reject) => { runUniqueApi('dataEncrypt', { base64: Base64.encode(dataJson), trigger: async (status: any, data: any) => { if (data && data.result) { const res = base64ToUint8Array(data.result).buffer resolve(res) } else { reject(Error('encrypt data fail')) addFrog({ url: '/debug/oralPK/dataEncryptFailed', params: { status: status, dataJson: dataJson }, flushFrog: false }) } } }, 'LeoSecure') }) } const base64ToUint8Array = (base64String: string): Uint8Array => { const padding = '='.repeat((4 - base64String.length % 4) % 4) const base64 = (base64String + padding) .replace(/-/g, '+') .replace(/_/g, '/') const rawData = Base64.atob(base64) const outputArray = new Uint8Array(rawData.length) for (let i = 0; i < rawData.length; ++i) { outputArray[i] = rawData.charCodeAt(i) } return outputArray } /** * 解密 ResponseBody */ export const DecryptData = (target: any, _key: string, descriptor: PropertyDescriptor) => { const oldMethod = descriptor.value const newMethod = async (...args: any) => { return oldMethod.apply(target, args).then(async (res: any) => { // @ts-ignore const buffer = btoa(String.fromCharCode.apply(null, new Uint8Array(res))) return await dataDecrypt(buffer) }).catch((err: any) => { throw err }) } descriptor.value = newMethod return descriptor } const dataDecrypt = (result: any) => { return new Promise(resolve => { runUniqueApi('dataDecrypt', { base64: result, trigger: (status: any, data: any) => { const decryptedData = JSON.parse(Base64.decode(data.result)) resolve(decryptedData) if (process.env.VUE_APP_CONFIG === 'test') { console.log('decrypted data: ', decryptedData) } } }, 'LeoSecure') }) }
res = flow.response.content # 将字节数组转换为 Base64 编码的字符串 base64_encoded = base64.b64encode(res).decode('utf-8') print(base64_encoded)
输出:y7KptyngpGugGocxaq6J8I5IOevoCyCJyfSyBRCvsA9fVeNKQdBN1FMynCeJODBM4fxyyvxunnezVxMQZV1oJhy9RBBg/j5bZjibxUFzdU0VwIuvZJLOQTrnyiHkQTiFMN1RBE3GhiNrsaJvYCjjC0h... 最终跟到webpack://leo-web-oral-pk/node_modules/@solar/solar-web-bridge/lib/native.js messageHandler[name]=dataDecrypt,传入encodeParam参数后跟不动了 encodeParam参数格式:base64
{"arguments":[{"base64":"y7KptyngpGugGocxaq6J8I5IOevoCyCJyfSyBRCvsA9fVeNKQdBN1FMynCeJODBM4fxyyvxunnezVxMQZV1oJhy9RBBg/j5bZjibxUFzdU0VwIuvZJLOQTrnyiHkQTiFMN1RBE3GhiNrsaJvYCjjC0h...","trigger":"dataDecrypt_1728641257271_14"}],"callback":"dataDecrypt_callback_1728641257271_15"}
这里处理完会返回一个base64的数据,最终解码得到明文 eyJwa0lkU3RyIjoiNjA5NDM0NDc... 补充:解密函数dataDecrypt似乎在LeoSecureWebViewApi.java里,不太懂java在这呢com.fenbi.android.leo.webapp.secure.commands.DataDecryptCommand$execute$1$decryptData$1.invokeSuspend
最终看调用了com.fenbi.android.leo.imgsearch.sdk.utils.e.c System.loadLibrary("ContentEncoder")加载了本地库更不知道咋整了,师傅有思路嘛
好像现在Server发向Client的响应包已经加密了,如果逆向工程没有什么进一步的突破,那改包方案就到此为止了
模拟器MuMu 抓包软件Reqable(安卓+Windows协同抓包)