Skip to content

Commit

Permalink
Added support for mifare classic value block operations (#86)
Browse files Browse the repository at this point in the history
  • Loading branch information
taichunmin authored Nov 16, 2023
2 parents 9f59ed1 + 0c81387 commit 66f988b
Show file tree
Hide file tree
Showing 4 changed files with 391 additions and 35 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"name": "chameleon-ultra.js",
"repository": "[email protected]:taichunmin/chameleon-ultra.js.git",
"unpkg": "dist/iife/index.min.js",
"version": "0.2.14",
"version": "0.2.15",
"bugs": {
"url": "https://github.com/taichunmin/chameleon-ultra.js/issues"
},
Expand Down
20 changes: 20 additions & 0 deletions pages/demos.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Demos

- [Demos](#demos)
- [device-settings.html](#device-settingshtml)
- [Features](#features)
- [mfkey32.html](#mfkey32html)
- [Features](#features-1)
- [Related links](#related-links)
- [mifare1k.html](#mifare1khtml)
- [Features](#features-2)
- [mifare-value.html](#mifare-valuehtml)

## [device-settings.html](https://taichunmin.idv.tw/chameleon-ultra.js/device-settings.html)

A ChameleonUltra tool to management the device info and settings.
Expand Down Expand Up @@ -50,3 +60,13 @@ A ChameleonUltra tool for mifare class 1k.
- Read/Write from Mifare Gen1a Tag (UID).
- Read/Write from Mifare Classic 1k, Gen2 Tag (CUID, FUID, UFUID).
- Import/Export from `.json`, `.bin`, `.eml`, `.mct` files.

- - -

## [mifare-value.html](https://taichunmin.idv.tw/chameleon-ultra.js/mifare-value.html)

![](https://i.imgur.com/jJ3pNvn.png)

在台灣有些 MIFARE Classic 卡片使用 value block 來儲存卡片的餘額,但是有些中國魔術卡不支援 value block 指令,所以無法使用這些魔術卡來複製 MIFARE Classic 卡片。這個工具可以讓你測試卡片是否支援 value block 指令。

A ChameleonUltra tool for MIFARE Classic value block commands. Some MIFARE Classic cards in Taiwan are using value block to store the balance of the card. But some chinese magic cards didn't support value block commands. So you can't use these magic cards to clone original MIFARE Classic cards. This tool can help you to test whether the card support value block commands or not.
204 changes: 204 additions & 0 deletions pug/src/mifare-value.pug
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
extends /include/bootstrapV4

block beforehtml
- const title = 'Mifare Value Block'

block style
meta(property="og:description", content="MIFARE Classic value block commands")
meta(property="og:locale", content="zh_TW")
meta(property="og:title", content=title)
meta(property="og:type", content="website")
meta(property="og:url", content=`${baseurl}mifare-value.html`)
style
:sass
[v-cloak]
display: none
body, .h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6
font-family: 'Noto Sans TC', sans-serif
.input-group-prepend > .input-group-text
width: 80px
.letter-spacing-n1px
&, .btn, textarea, select, input
letter-spacing: -1px
.text-sm
font-size: 0.875rem
block content
#app.my-3.container.text-monospace(v-cloak)
h4.mb-3.text-center.letter-spacing-n1px #[.bgicon.bgicon-chameleon-ultra.mr-1] #{title}
.form-group.letter-spacing-n1px
label Connect method:
.input-group.input-group-sm.mb-3
select.form-control(v-model="ls.adapter")
option(value="ble") BLE (PC & Android)
option(value="usb") USB Serial (PC only)
.input-group-append: button.btn.btn-outline-secondary(@click="btnAdapterTips") #[i.fa.fa-fw.fa-question]
.form-group.letter-spacing-n1px
label Value Block: Source
.input-group.input-group-sm.mb-2.was-validated
.input-group-prepend: span.input-group-text.justify-content-center Block
input.form-control(type="number", min="0", max="63", required, v-model.number="ss.src.block")
.input-group-append: span.input-group-text Sector {{ blockToSector(ss?.src?.block ?? 0) }}
.input-group.input-group-sm.mb-2
.input-group-prepend: span.input-group-text.justify-content-center KeyType
select.form-control.form-control-sm(v-model.number="ss.src.keyType")
option(value="0") Key A
option(value="1") Key B
.input-group.input-group-sm.mb-2.was-validated
.input-group-prepend: span.input-group-text.justify-content-center Key
input.form-control(type="text", required, pattern="[\\dA-Fa-f]{12}", v-model="ss.src.key")
.form-group.letter-spacing-n1px
label Value Block: Target
.custom-control.custom-checkbox.mb-2
input.custom-control-input#ss-srcAsDst(type="checkbox", v-model="ss.srcAsDst")
label.custom-control-label(for="ss-srcAsDst") Use source as target
template(v-if="ss.srcAsDst")
.input-group.input-group-sm.mb-2
.input-group-prepend: span.input-group-text.justify-content-center Block
input.form-control(type="number", v-model.number="ss.src.block", disabled)
.input-group-append: span.input-group-text Sector {{ blockToSector(ss?.src?.block ?? 0) }}
.input-group.input-group-sm.mb-2
.input-group-prepend: span.input-group-text.justify-content-center KeyType
select.form-control.form-control-sm(v-model.number="ss.src.keyType", disabled)
option(value="0") Key A
option(value="1") Key B
.input-group.input-group-sm.mb-2
.input-group-prepend: span.input-group-text.justify-content-center Key
input.form-control(type="text", v-model="ss.src.key", disabled)
template(v-else)
.input-group.input-group-sm.mb-2.was-validated
.input-group-prepend: span.input-group-text.justify-content-center Block
input.form-control(type="number", min="0", max="63", required, v-model.number="ss.dst.block")
.input-group-append: span.input-group-text Sector {{ blockToSector(ss?.dst?.block ?? 0) }}
.input-group.input-group-sm.mb-2
.input-group-prepend: span.input-group-text.justify-content-center KeyType
select.form-control.form-control-sm(v-model.number="ss.dst.keyType")
option(value="0") Key A
option(value="1") Key B
.input-group.input-group-sm.mb-2.was-validated
.input-group-prepend: span.input-group-text.justify-content-center Key
input.form-control(type="text", required, pattern="[\\dA-Fa-f]{12}", v-model="ss.dst.key")
.form-group.letter-spacing-n1px
label Value Block: Operation
.input-group.input-group-sm.mb-2.was-validated
.input-group-prepend: span.input-group-text.justify-content-center Value
input.form-control(type="number", required, v-model.number="ss.operand")
.row.mx-n1.mb-2
.col.px-1: button.btn.btn-sm.btn-block.btn-outline-success(@click="btnGetValue") #[i.fa.fa-fw.fa-sign-out] Get Source
.col.px-1: button.btn.btn-sm.btn-block.btn-outline-primary(@click="btnSetValue") #[i.fa.fa-fw.fa-sign-in] Set Source
button.btn.btn-sm.btn-block.btn-outline-dark.mb-2(@click="btnManipulateValue(Mf1VblockOperator.INCREMENT)") #[i.fa.mr-1.fa-plus-square] Increase Value
button.btn.btn-sm.btn-block.btn-outline-dark.mb-2(@click="btnManipulateValue(Mf1VblockOperator.DECREMENT)") #[i.fa.mr-1.fa-minus-square] Decrease Value
button.btn.btn-sm.btn-block.btn-outline-dark.mb-2(@click="btnManipulateValue(Mf1VblockOperator.RESTORE)") #[i.fa.mr-1.fa-clone] Clone Value

block script
script(crossorigin="anonymous", src="https://cdn.jsdelivr.net/npm/joi@17/dist/joi-browser.min.js")
script.
const { Buffer, ChameleonUltra, DeviceMode, Mf1KeyType, Mf1VblockOperator, WebbleAdapter, WebserialAdapter } = ChameleonUltraJS // eslint-disable-line
const ultraUsb = new ChameleonUltra(true)
ultraUsb.use(new WebserialAdapter())
const ultraBle = new ChameleonUltra(true)
ultraBle.use(new WebbleAdapter())

window.vm = new Vue({
el: '#app',
data: {
ls: {
adapter: 'ble',
},
ss: {
src: { block: 4, keyType: 0, key: 'FFFFFFFFFFFF' },
dst: { block: 4, keyType: 0, key: 'FFFFFFFFFFFF' },
operand: 0,
srcAsDst: true,
},
},
async mounted () {
// 自動儲存功能
for (const [storage, key] of [[localStorage, 'ls'], [sessionStorage, 'ss']]) {
try {
const saved = JSON5.parse(storage.getItem(location.pathname))
if (saved) this.$set(this, key, _.merge(this[key], saved))
} catch (err) {}
this.$watch(key, () => {
storage.setItem(location.pathname, JSON5.stringify(this[key]))
}, { deep: true })
}
},
computed: {
ultra () {
return this.ls.adapter === 'usb' ? ultraUsb : ultraBle
},
},
methods: {
async btnAdapterTips () {
await Swal.fire({
title: 'Browser & OS',
html: '<strong class="text-success">BLE</strong> is available in ChromeOS, Chrome for Android 6.0, Mac (Chrome 56) and Windows 10 (Chrome 70), <a class="btn-link" target="_blank" href="https://apps.apple.com/app/bluefy-web-ble-browser/id1492822055">Bluefy</a> for iPhone and iPad.<hr><strong class="text-success">USB</strong> is available on all desktop platforms (ChromeOS, Linux, macOS, and Windows) in Chrome 89.',
})
},
async cmdVblockGet (src) {
src = this.mf1ParseBlockKey(src)
const ultra = this.ultra
await ultra.cmdChangeDeviceMode(DeviceMode.READER)
const res = await ultra.mf1VblockGetValue(src)
await Swal.fire({ icon: 'success', title: 'Success', text: `block[${src.block}] = ${JSON.stringify(res)}` })
},
async btnGetValue () {
try {
this.showLoading({ text: 'Processing...' })
await this.cmdVblockGet(this.ss.src)
} catch (err) {
console.error(err)
await Swal.fire({ icon: 'error', title: 'Failed to get value from src', text: err.message })
}
},
async btnSetValue () {
try {
this.showLoading({ text: 'Processing...' })
const src = this.mf1ParseBlockKey(this.ss.src)
const ultra = this.ultra
await ultra.cmdChangeDeviceMode(DeviceMode.READER)
await ultra.mf1VblockSetValue(src, { adr: src.block, value: this.ss.operand })
await this.cmdVblockGet(this.ss.src)
} catch (err) {
console.error(err)
await Swal.fire({ icon: 'error', title: 'Failed to set value to src', text: err.message })
}
},
async btnManipulateValue (operator) {
try {
this.showLoading({ text: 'Processing...' })
const src = this.mf1ParseBlockKey(this.ss.src)
const dst = this.ss.srcAsDst ? src : this.mf1ParseBlockKey(this.ss.dst)
const operand = this.ss.operand
const ultra = this.ultra
await ultra.cmdChangeDeviceMode(DeviceMode.READER)
await ultra.cmdMf1VblockManipulate(src, operator, operand, dst)
await this.cmdVblockGet(this.ss.src)
} catch (err) {
console.error(err)
await Swal.fire({ icon: 'error', title: `Failed to ${Mf1VblockOperator[operator]} value`, text: err.message })
}
},
mf1ParseBlockKey ({ block, keyType, key }) {
return {
block,
keyType: keyType === 1 ? Mf1KeyType.KEY_B : Mf1KeyType.KEY_A,
key: Buffer.from(key, 'hex'),
}
},
blockToSector (block = 0) {
return block < 128 ? block >>> 2 : 24 + (block >>> 4)
},
showLoading (opts = {}) {
opts = {
allowOutsideClick: false,
showConfirmButton: false,
...opts,
}
if (Swal.isVisible()) return Swal.update(_.omit(opts, ['progressStepsDistance']))
Swal.fire({ ...opts, didRender: () => { Swal.showLoading() } })
},
},
})

Loading

0 comments on commit 66f988b

Please sign in to comment.