Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

包加密的 #71

Open
Jaffe2718 opened this issue Oct 11, 2024 · 26 comments
Open

包加密的 #71

Jaffe2718 opened this issue Oct 11, 2024 · 26 comments

Comments

@Jaffe2718
Copy link

721ae3e15d0ea58ca96370bcf04c4d2 好像现在Server发向Client的响应包已经加密了,如果逆向工程没有什么进一步的突破,那改包方案就到此为止了

模拟器MuMu
抓包软件Reqable(安卓+Windows协同抓包)

@xperiavnc
Copy link

寄了

@ZeroQing89
Copy link

很蓝的呐

@sd0ric4
Copy link
Collaborator

sd0ric4 commented Oct 11, 2024

在逆了

@ZeroQing89
Copy link

在逆了

逆向大佬竟在我身边

@sd0ric4
Copy link
Collaborator

sd0ric4 commented Oct 11, 2024

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')
  })
}

@sd0ric4
Copy link
Collaborator

sd0ric4 commented Oct 11, 2024

翻出来一个

@sd0ric4
Copy link
Collaborator

sd0ric4 commented Oct 11, 2024

不知道是不是

@ZeroQing89
Copy link

不知道是不是

先测试一下

@GSQZ
Copy link
Contributor

GSQZ commented Oct 11, 2024

在逆了

66666

1 similar comment
@GSQZ
Copy link
Contributor

GSQZ commented Oct 11, 2024

在逆了

66666

@ZeroQing89
Copy link

Base64加密???

@sd0ric4
Copy link
Collaborator

sd0ric4 commented Oct 11, 2024

啊有大佬写了笔记,观摩中,但大佬好像还没写完
https://github.com/xmexg/xyks

@ZeroQing89
Copy link

啊有大佬写了笔记,观摩中,但大佬好像还没写完 https://github.com/xmexg/xyks

那个大佬似乎也没能全部解完

@Jaffe2718
Copy link
Author

在逆了

66666

我倒是有个不用逆的思路,不清楚行不行
image
你看看这个PUT请求,是不是有costTime字段,我估计单位是毫秒,但是无所谓,直接改成0即可。
如果成功了我感觉效果是做题依然是手动做题,但是用时会被强行改为0,你可以试试

@cr4n5
Copy link
Owner

cr4n5 commented Oct 11, 2024

在逆了

66666

我倒是有个不用逆的思路,不清楚行不行 image 你看看这个PUT请求,是不是有costTime字段,我估计单位是毫秒,但是无所谓,直接改成0即可。 如果成功了我感觉效果是做题依然是手动做题,但是用时会被强行改为0,你可以试试

没用的,这只会在本地显示,服务器上未有任何更改

@ZeroQing89
Copy link

在逆了

66666

我倒是有个不用逆的思路,不清楚行不行 image 你看看这个PUT请求,是不是有costTime字段,我估计单位是毫秒,但是无所谓,直接改成0即可。 如果成功了我感觉效果是做题依然是手动做题,但是用时会被强行改为0,你可以试试

找不到接口感觉没用啊

@Jaffe2718
Copy link
Author

@ZeroQing89 感觉本地也够装一波了,但是目前不清楚怎么改

@ZeroQing89
Copy link

@ZeroQing89 感觉本地也够装一波了,但是目前不清楚怎么改

本地就没意思了,这和F12调试侠有什么两样啊😂😂😂

@ZeroQing89
Copy link

现在最大的问题就是逆向成本有点高

@xingyuxinyuan
Copy link

很难的啦

@masknull
Copy link

masknull commented Oct 11, 2024

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参数后跟不动了

微信截图_20241011181602

encodeParam参数格式:base64
{"arguments":[{"base64":"y7KptyngpGugGocxaq6J8I5IOevoCyCJyfSyBRCvsA9fVeNKQdBN1FMynCeJODBM4fxyyvxunnezVxMQZV1oJhy9RBBg/j5bZjibxUFzdU0VwIuvZJLOQTrnyiHkQTiFMN1RBE3GhiNrsaJvYCjjC0h...","trigger":"dataDecrypt_1728641257271_14"}],"callback":"dataDecrypt_callback_1728641257271_15"}

这里处理完会返回一个base64的数据,最终解码得到明文
eyJwa0lkU3RyIjoiNjA5NDM0NDc...

补充:解密函数dataDecrypt似乎在LeoSecureWebViewApi.java里,不太懂java

@xperiavnc
Copy link

抖音上已经有人研究出跳过答题了

@ZQBCWG
Copy link

ZQBCWG commented Oct 12, 2024

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参数后跟不动了

微信截图_20241011181602

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

@masknull
Copy link

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参数后跟不动了
微信截图_20241011181602
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")加载了本地库更不知道咋整了,师傅有思路嘛

@Jaffe2718
Copy link
Author

各位大佬逆向完了能不能封装成python库呀,这样方便偷懒,要是真的有这个库就舒服了
类似这样:

pip install xiaoyuan_toolkit
import xiaoyuan_tookit

# 抓包...
packet_json: dict = xiaoyuan_tookit.decrypt(b'被加密的内容')

# 解析并改包...
enc = xiaoyuan_toolkit.encrypt(packet_json)  # 重新加密
# 再把改后的包给客户端

@ZQBCWG
Copy link

ZQBCWG commented Oct 12, 2024

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参数后跟不动了
微信截图_20241011181602
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")加载了本地库更不知道咋整了,师傅有思路嘛

已经搞完了

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants