Skip to content

Commit

Permalink
fix(check-in): update form and corresponding encryption algos
Browse files Browse the repository at this point in the history
  • Loading branch information
beetcb committed Oct 27, 2021
1 parent 018c6ab commit 6e3fd54
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 133 deletions.
8 changes: 2 additions & 6 deletions core/src/crawler/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,8 @@ import log from '../utils/logger.js'
import ocr from './capcha.js'

import type { Response } from 'node-fetch'
import type {
DefaultProps,
EdgeCasesSchools,
SchoolConfOpts,
UserConfOpts,
} from '../types/conf'
import { DefaultProps, EdgeCasesSchools } from '../compatibility/edge-case.js'
import { SchoolConfOpts, UserConfOpts } from '../types/conf'
import type { HandleCookieOptions } from '../types/cookie'
import type { StringKV } from '../types/helper'

Expand Down
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,7 @@
"ts-jest": "^27.0.4",
"typedoc-github-wiki-theme": "^0.3.0",
"typedoc-plugin-markdown": "^3.8.1",
"typescript": "^4.4.4"
},
"dependencies": {
"typescript": "^4.4.4",
"dprint": "^0.18.1"
}
}
2 changes: 1 addition & 1 deletion plugins/check-in/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"dependencies": {
"cea-core": "2.0.1",
"node-fetch": "^2.6.5",
"ts-md5":"1.2.9",
"ts-md5": "1.2.9",
"uuid": "^8.3.2"
},
"devDependencies": {
Expand Down
170 changes: 74 additions & 96 deletions plugins/check-in/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { CampusphereEndpoint } from 'cea-core'
import { LogInfoKeys } from './types.js'
import { Md5 } from 'ts-md5/dist/md5'
import { handleCookie, sstore } from 'cea-core'
import crypto from 'crypto'
import fetch from 'node-fetch'
import * as uuid from 'uuid'
import { LogInfoKeys, PostFormBody } from './types.js'

import type {
CookieRawObject,
Expand All @@ -15,19 +14,32 @@ import type {
} from 'cea-core'
import type {
AllSignTasks,
CpdailyExtension,
CpdailyExtensionEncrypted,
bodyStringEncrypted,
GlobalLogInfo,
LogInfo,
SignForm,
SignExtensionBody,
SignFormBody,
SignHashBody,
SignTask,
SignTaskDetail,
} from './types'
import { json } from 'stream/consumers'
import { version } from 'os'

export class CheckIn {
static readonly VERSION = {
app: '9.0.12',
version: 'first_v2',
calVersion: 'firstv',
}
static readonly EXTENSION_ENCRYPT = {
key: 'b3L26XNL',
iv: Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]),
algo: 'des-cbc',
}
static readonly FORMBODY_ENCRYPT = {
key: 'ytUQ7l2ZZu8mLvJZ',
iv: Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7]),
algo: 'aes-128-cbc',
}

private headers: StringKV
private user: UserConfOpts
private school: SchoolConfOpts
Expand Down Expand Up @@ -96,15 +108,15 @@ export class CheckIn {

const placeList = signPlaceSelected[0]
const isSignAtHome = !Boolean(school.defaultAddr)
;[longitude, latitude, position] = isSignAtHome
? this.user.addr
: [placeList.longitude, placeList.latitude, school.defaultAddr]
;[longitude, latitude, position] = isSignAtHome
? this.user.addr
: [placeList.longitude, placeList.latitude, school.defaultAddr]

const extraFieldItems = this.fillExtra(extraField)
const extraFieldItems = CheckIn.fillExtra(extraField)

const form: SignForm = {
longitude: this.fixedFloatRight(longitude),
latitude: this.fixedFloatRight(latitude),
const formBody: SignFormBody = {
longitude: CheckIn.fixedFloatRight(longitude),
latitude: CheckIn.fixedFloatRight(latitude),
isMalposition: isSignAtHome ? 1 : 0,
abnormalReason: '',
signPhotoUrl: '',
Expand All @@ -114,60 +126,56 @@ export class CheckIn {
signInstanceWid,
extraFieldItems,
}
const bodyString = this.Aes_encrpt(JSON.stringify(form))
const signbody = {
appVersion: '9.0.12',
bodyString: bodyString,
const bodyString = CheckIn.formBodyEncrypt(formBody)

const signHashBody: SignHashBody = {
appVersion: CheckIn.VERSION.app,
bodyString,
deviceId: uuid.v1(),
lat: form.latitude.toString(),
lon: form.longitude.toString(),
lat: formBody.latitude,
lon: formBody.longitude,
model: 'Cock',
systemName: 'android',
systemVersion: 'Cock',
systemVersion: '11',
userId: this.user.username,
}
let body_to_string = ''
for (const key in signbody) {
body_to_string += '${key}=${signBody[key]}&'
}
body_to_string += "ytUQ7l2ZZu8mLvJZ"
const sign = Md5.hashStr(body_to_string)
/* 按照抓包顺序
const final_form = {
lon: signbody.lon,
version: 'first_v2',
calVersion: 'firstv',
deviceId: signbody.deviceId,
userId: signbody.userId,
systemName: signbody.systemName,
bodyString: bodyString,
lat: signbody.lat,
systemVersion: signbody.systemVersion,
appVersion: signbody.appVersion,
module:signbody.model,
sign: sign,

// Hack to ensure signHashBody is alphabet-ordered
const signExtensionBody: SignExtensionBody & { bodyString: undefined } = {
...signHashBody,
bodyString: undefined,
}
*/
const final_form = {
...signbody,
version: 'first_v2',
calVersion: 'firstv',
sign: sign,

const signHash = crypto
.createHash('md5')
.update(
`${
new URLSearchParams(signHashBody as any).toString()
}&${CheckIn.FORMBODY_ENCRYPT.key}`,
)
.digest('hex')

const postBody: PostFormBody = {
sign: signHash,
version: CheckIn.VERSION.version,
calVersion: CheckIn.VERSION.calVersion,
...signHashBody,
}
headers['Cpdaily-Extension'] = this.extention(form)

headers['Cpdaily-Extension'] = CheckIn.extensionEncrypt(signExtensionBody)
res = await fetch(
`${school.campusphere}${CampusphereEndpoint.submitSign}`,
{
headers,
method: 'POST',
body: JSON.stringify(final_form),
body: JSON.stringify(postBody),
},
)
const result = (await res.json()) as any

const logInfo: LogInfo = {
[LogInfoKeys.result]: result.message,
[LogInfoKeys.addr]: form.position,
[LogInfoKeys.addr]: formBody.position,
}

// Hide sensitive info on github actions, cause it's public by default
Expand All @@ -179,7 +187,7 @@ export class CheckIn {
return logInfo
}

private fixedFloatRight(floatStr: string): number {
private static fixedFloatRight(floatStr: string): number {
return parseFloat(
floatStr.replace(
/(\d+\.\d{5})(\d{1})(.*)/,
Expand All @@ -189,9 +197,9 @@ export class CheckIn {
}

// select right item with content&wid
private fillExtra(
private static fillExtra(
extraField: SignTaskDetail['extraField'],
): SignForm['extraFieldItems'] {
): SignFormBody['extraFieldItems'] {
return extraField.map((e) => {
let chosenWid: string
const normal = e.extraFieldItems.filter((i) => {
Expand All @@ -205,52 +213,21 @@ export class CheckIn {
})
}

// construct and encrypte Cpdaily_Extension for header
private extention(form: SignForm) {
const Cpdaily_Extension: CpdailyExtension = {
lon: form.longitude.toString(),
model: 'Cock',
appVersion: '9.0.12',
systemVersion: '11',
userId: this.user.username,
systemName: 'android',
lat: form.latitude.toString(),
deviceId: uuid.v1(),
}
return this.encrypt(Cpdaily_Extension)
}

private encrypt(ce: CpdailyExtension): CpdailyExtensionEncrypted {
const algorithm = 'des-cbc'
const key = 'b3L26XNL'
const iv = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]) // Initialization vector.

const cipher = crypto.createCipheriv(algorithm, key, iv)

let encrypted = cipher.update(JSON.stringify(ce), 'utf8', 'base64')
private static extensionEncrypt(body: SignExtensionBody): string {
const { algo, key, iv } = CheckIn.EXTENSION_ENCRYPT
const cipher = crypto.createCipheriv(algo, key, iv)
let encrypted = cipher.update(JSON.stringify(body), 'utf8', 'base64')
encrypted += cipher.final('base64')
return encrypted
}
private Aes_encrpt(ce: string): bodyStringEncrypted {
const algorithm = 'aes-cbc'
const key = 'ytUQ7l2ZZu8mLvJZ'
const iv = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7])
const cipher = crypto.createCipheriv(algorithm, key, iv)
let encrypted = cipher.update(JSON.stringify(ce), 'utf8', 'base64')

private static formBodyEncrypt(body: SignFormBody): string {
const { algo, key, iv } = CheckIn.FORMBODY_ENCRYPT
const cipher = crypto.createCipheriv(algo, key, iv)
let encrypted = cipher.update(JSON.stringify(body), 'utf8', 'base64')
encrypted += cipher.final('base64')
return encrypted
}

private decrypt() {
const algorithm = 'des-cbc'
const key = 'b3L26XNL'
const iv = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]) // Initialization vector.
const decipher = crypto.createDecipheriv(algorithm, key, iv)
const encrypted = 'long base 64'

let decrypted = decipher.update(encrypted, 'base64', 'utf8')
decrypted += decipher.final('utf8')
}
}

export async function checkIn() {
Expand Down Expand Up @@ -280,8 +257,9 @@ async function signIn(users: UsersConf): Promise<GlobalLogInfo> {
logs[i.alias] = result
} else {
logs[i.alias] = {
[LogInfoKeys.result as string]: `已完成:${curTask.signedTasks[0].taskName
}`,
[LogInfoKeys.result as string]: `已完成:${
curTask.signedTasks[0].taskName
}`,
}
}
}
Expand Down
68 changes: 41 additions & 27 deletions plugins/check-in/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,8 @@ export type SignTaskDetail = {
}>
}>
}

export enum LogInfoKeys {
result = '签到结果',
addr = '签到地址',
}

export type LogInfo = {
[K in LogInfoKeys]?: string
}

export type GlobalLogInfo = { [key: string]: LogInfo }

export type SignForm = {
/**用于记录签到表单的对象 */
export type SignFormBody = {
signInstanceWid: string
longitude: number
latitude: number
Expand All @@ -47,20 +36,45 @@ export type SignForm = {
uaIsCpadaily: true
isMalposition: 1 | 0
}
/**用于提交签到的对象 */
export type PostFormBody = {
/**由 SignHashBody 序列字符 MD5 Hash 处理得到*/
sign: string
/**版本号:来源未知 */
calVersion: string
/**版本号:来源未知 */
version: string
/**应用版本 */
appVersion: string
/**加密后的 SignFormBody */
bodyString: string
/**设备唯一标识符 */
deviceId: string
/**签到维度 */
lat: number
/**签到经度 */
lon: number
/**手机型号 */
model: string
/**手机系统名 */
systemName: string
/**手机系统版本 */
systemVersion: string
/**用户学号 */
userId: string
}
/**用于构造 Sign Hash 字符的对象*/
export type SignHashBody = Omit<PostFormBody, 'sign' | 'calVersion' | 'version'>
/**用于构造 Cpdaily_Extension Header 字符的对象 */
export type SignExtensionBody = Omit<SignHashBody, 'bodyString'>

export enum LogInfoKeys {
result = '签到结果',
addr = '签到地址',
}

export type CpdailyExtension = {
[
K in
| 'lon'
| 'model'
| 'appVersion'
| 'systemVersion'
| 'userId'
| 'systemName'
| 'lat'
| 'deviceId'
]: string
export type LogInfo = {
[K in LogInfoKeys]?: string
}

export type CpdailyExtensionEncrypted = string
export type bodyStringEncrypted = string
export type GlobalLogInfo = { [key: string]: LogInfo }

0 comments on commit 6e3fd54

Please sign in to comment.