2025-01-01 17:27:12 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"os"
|
|
|
|
|
"os/exec"
|
|
|
|
|
"strings"
|
|
|
|
|
"strconv"
|
|
|
|
|
"math/rand"
|
|
|
|
|
"encoding/hex"
|
|
|
|
|
"time"
|
|
|
|
|
"net" /* for MAC address parsing */
|
|
|
|
|
"fyne.io/fyne/v2"
|
|
|
|
|
"fyne.io/fyne/v2/app"
|
|
|
|
|
"fyne.io/fyne/v2/container"
|
|
|
|
|
"fyne.io/fyne/v2/widget"
|
|
|
|
|
"fyne.io/fyne/v2/layout"
|
|
|
|
|
"fyne.io/fyne/v2/dialog"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
/* su-executor, returns standard output */
|
|
|
|
|
func suexec(cmdstr string) string {
|
|
|
|
|
out, err := exec.Command("su", "-c", cmdstr).Output()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err.Error()
|
|
|
|
|
}
|
|
|
|
|
return string(out)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* calculate Luhn checksum over 14 IMEI digits */
|
|
|
|
|
func calcLuhn(num string) int {
|
|
|
|
|
sum := 0
|
|
|
|
|
dd := 0
|
|
|
|
|
for i, d := range num {
|
|
|
|
|
if d >= 48 && d < 58 {
|
|
|
|
|
dd = int(d) - 48
|
|
|
|
|
if i%2 == 1 {
|
|
|
|
|
dd += dd
|
|
|
|
|
if dd > 9 {
|
|
|
|
|
dd -= 9
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
sum += dd
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
sum %= 10
|
|
|
|
|
if sum != 0 {
|
|
|
|
|
sum = 10 - sum
|
|
|
|
|
}
|
|
|
|
|
return sum
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* generate an IMEI by TAC */
|
|
|
|
|
func genimei(tac string) string {
|
|
|
|
|
var imeisb strings.Builder
|
|
|
|
|
imeisb.WriteString(tac) /* start with the prefix */
|
|
|
|
|
for i:=0; i < 14; i++ {
|
|
|
|
|
imeisb.WriteString(strconv.Itoa(rand.Intn(10)))
|
|
|
|
|
}
|
|
|
|
|
imei := imeisb.String()[:14] /* only take the first 14 digits of the result */
|
|
|
|
|
return imei + strconv.Itoa(calcLuhn(imei))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* generate a MAC address by TAC/prefix */
|
|
|
|
|
func genmac(pref string) string {
|
|
|
|
|
var macsb strings.Builder
|
|
|
|
|
bytes := make([]byte, 6)
|
|
|
|
|
rand.Read(bytes)
|
|
|
|
|
macsb.WriteString(strings.ReplaceAll(pref, ":", ""))
|
|
|
|
|
macsb.WriteString(hex.EncodeToString(bytes))
|
|
|
|
|
premac := macsb.String()[:12] /* only use the first 12 hex digits of the result */
|
|
|
|
|
macbytes, err := hex.DecodeString(premac)
|
|
|
|
|
if err == nil {
|
|
|
|
|
return strings.ToUpper(net.HardwareAddr(macbytes).String())
|
|
|
|
|
} else {
|
|
|
|
|
return "00:00:00:00:00:00"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* GUI entry point */
|
|
|
|
|
func main() {
|
|
|
|
|
/* init the app */
|
|
|
|
|
rand.Seed(time.Now().UTC().UnixNano())
|
|
|
|
|
lexiapp := app.New()
|
|
|
|
|
mainwin := lexiapp.NewWindow("lexipwn")
|
|
|
|
|
|
|
|
|
|
/* check for root access/su availability */
|
|
|
|
|
_, err := exec.LookPath("su")
|
|
|
|
|
if err != nil {
|
|
|
|
|
dialog.ShowInformation("lexipwn", "Root access is required to run lexipwn!", mainwin)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* extract lexipwn-cli to an accessible location */
|
|
|
|
|
|
|
|
|
|
clipath := lexiapp.Storage().RootURI().Path() + "/lexipwn"
|
|
|
|
|
os.WriteFile(clipath, resourceLexipwn.StaticContent, 0777)
|
|
|
|
|
|
|
|
|
|
/* input fields and their labels */
|
|
|
|
|
|
|
|
|
|
imei1field := widget.NewEntry()
|
|
|
|
|
imei1lbl := widget.NewLabel("IMEI 1")
|
|
|
|
|
imei2field := widget.NewEntry()
|
|
|
|
|
imei2lbl := widget.NewLabel("IMEI 2")
|
|
|
|
|
psnfield := widget.NewEntry()
|
|
|
|
|
psnlbl := widget.NewLabel("PSN")
|
|
|
|
|
dsnfield := widget.NewEntry()
|
|
|
|
|
dsnlbl := widget.NewLabel("DSN")
|
|
|
|
|
wlanmacfield := widget.NewEntry()
|
|
|
|
|
wlanmaclbl := widget.NewLabel("WLAN MAC address")
|
|
|
|
|
btmacfield := widget.NewEntry()
|
|
|
|
|
btmaclbl := widget.NewLabel("BT MAC address")
|
|
|
|
|
pcbcfgfield := widget.NewEntry()
|
|
|
|
|
pcbcfglbl := widget.NewLabel("PCB config")
|
|
|
|
|
devcfgfield := widget.NewEntry()
|
|
|
|
|
devcfglbl := widget.NewLabel("Dev config")
|
|
|
|
|
skufield := widget.NewEntry()
|
|
|
|
|
skulbl := widget.NewLabel("SKU")
|
|
|
|
|
coofield := widget.NewEntry()
|
|
|
|
|
coolbl := widget.NewLabel("COO")
|
|
|
|
|
dpcfield := widget.NewEntry()
|
|
|
|
|
dpclbl := widget.NewLabel("DPC")
|
|
|
|
|
wccfield := widget.NewEntry()
|
|
|
|
|
wcclbl := widget.NewLabel("WCC")
|
|
|
|
|
bootmodelbl := widget.NewLabel("Boot mode")
|
|
|
|
|
bootmodefield := widget.NewRadioGroup([]string{"normal", "factory"}, func(val string){})
|
|
|
|
|
tacprefixfield := widget.NewEntry()
|
|
|
|
|
tacprefixlbl := widget.NewLabel("IMEI prefix/TAC")
|
|
|
|
|
macprefixfield := widget.NewEntry()
|
|
|
|
|
macprefixlbl := widget.NewLabel("MAC prefix")
|
|
|
|
|
atcmdfield := widget.NewEntry()
|
|
|
|
|
atcmdres := widget.NewMultiLineEntry()
|
|
|
|
|
atcmdres.SetMinRowsVisible(7)
|
|
|
|
|
atcmdres.Wrapping = fyne.TextWrapWord
|
|
|
|
|
|
|
|
|
|
fieldgrid := container.New(layout.NewGridLayout(2), imei1lbl, imei1field,
|
|
|
|
|
imei2lbl, imei2field, psnlbl, psnfield, dsnlbl, dsnfield,
|
|
|
|
|
wlanmaclbl, wlanmacfield, btmaclbl, btmacfield, pcbcfglbl, pcbcfgfield,
|
|
|
|
|
devcfglbl, devcfgfield, skulbl, skufield, coolbl, coofield, dpclbl, dpcfield,
|
|
|
|
|
wcclbl, wccfield)
|
|
|
|
|
|
|
|
|
|
/* control buttons */
|
|
|
|
|
|
|
|
|
|
loadbtn := widget.NewButton("Load devinfo", func() {
|
|
|
|
|
rawlist := suexec(clipath + " listps")
|
|
|
|
|
for _, entry := range strings.Split(rawlist, "\n") {
|
|
|
|
|
eparts := strings.Split(entry, "\t")
|
|
|
|
|
if len(eparts) > 2 {
|
|
|
|
|
name := strings.TrimRight(eparts[1], "\x00")
|
|
|
|
|
val := strings.TrimRight(eparts[2], "\x00")
|
|
|
|
|
switch name { /* loading by name */
|
|
|
|
|
case "bootmode":
|
|
|
|
|
if val == "factory" {
|
|
|
|
|
bootmodefield.SetSelected("factory")
|
|
|
|
|
} else {
|
|
|
|
|
bootmodefield.SetSelected("normal")
|
|
|
|
|
}
|
|
|
|
|
case "imei1":
|
|
|
|
|
imei1field.SetText(val)
|
|
|
|
|
case "imei2":
|
|
|
|
|
imei2field.SetText(val)
|
|
|
|
|
case "psn":
|
|
|
|
|
psnfield.SetText(val)
|
|
|
|
|
case "dsn":
|
|
|
|
|
dsnfield.SetText(val)
|
|
|
|
|
case "wlan_mac1":
|
|
|
|
|
wlanmacfield.SetText(val)
|
|
|
|
|
case "bt_addr":
|
|
|
|
|
btmacfield.SetText(val)
|
|
|
|
|
case "pcbcfg":
|
|
|
|
|
pcbcfgfield.SetText(val)
|
|
|
|
|
case "devcfg":
|
|
|
|
|
devcfgfield.SetText(val)
|
|
|
|
|
case "sku":
|
|
|
|
|
skufield.SetText(val)
|
|
|
|
|
case "coo":
|
|
|
|
|
coofield.SetText(val)
|
|
|
|
|
case "dpc":
|
|
|
|
|
dpcfield.SetText(val)
|
|
|
|
|
case "wcc":
|
|
|
|
|
wccfield.SetText(val)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
savebtn := widget.NewButton("Save devinfo", func() {
|
|
|
|
|
dialog.ShowConfirm("Confirm operation", "Overwrite devinfo partition?", func(choice bool) {
|
|
|
|
|
if choice == true {
|
|
|
|
|
var fieldset strings.Builder
|
|
|
|
|
fieldset.WriteString(" bootmode " + bootmodefield.Selected)
|
|
|
|
|
fieldset.WriteString(" imei1 " + imei1field.Text)
|
|
|
|
|
fieldset.WriteString(" imei2 " + imei2field.Text)
|
|
|
|
|
fieldset.WriteString(" psn " + psnfield.Text)
|
|
|
|
|
fieldset.WriteString(" dsn " + dsnfield.Text)
|
|
|
|
|
fieldset.WriteString(" wlan_mac1 " + wlanmacfield.Text)
|
|
|
|
|
fieldset.WriteString(" bt_addr " + btmacfield.Text)
|
|
|
|
|
fieldset.WriteString(" pcbcfg " + pcbcfgfield.Text)
|
|
|
|
|
fieldset.WriteString(" devcfg " + devcfgfield.Text)
|
|
|
|
|
fieldset.WriteString(" sku " + skufield.Text)
|
|
|
|
|
fieldset.WriteString(" coo " + coofield.Text)
|
|
|
|
|
fieldset.WriteString(" dpc " + dpcfield.Text)
|
|
|
|
|
fieldset.WriteString(" wcc " + wccfield.Text)
|
|
|
|
|
suexec(clipath + " setpstype bootmode DIUS")
|
|
|
|
|
res := suexec(clipath + " setpsval" + fieldset.String())
|
|
|
|
|
dialog.ShowInformation("Operation result", res, mainwin)
|
|
|
|
|
}
|
|
|
|
|
}, mainwin)
|
|
|
|
|
})
|
|
|
|
|
fixbtn := widget.NewButton("Fix IMEI CPSHA", func() {
|
|
|
|
|
dialog.ShowInformation("Operation result", suexec(clipath + " fiximeisha"), mainwin)
|
|
|
|
|
})
|
|
|
|
|
var tabs *container.AppTabs
|
|
|
|
|
randomimei1btn := widget.NewButton("Randomize IMEI 1", func() {
|
|
|
|
|
imei1field.SetText(genimei(tacprefixfield.Text))
|
|
|
|
|
tabs.SelectIndex(0)
|
|
|
|
|
})
|
|
|
|
|
randomimei2btn := widget.NewButton("Randomize IMEI 2", func() {
|
|
|
|
|
imei2field.SetText(genimei(tacprefixfield.Text))
|
|
|
|
|
tabs.SelectIndex(0)
|
|
|
|
|
})
|
|
|
|
|
randomwlanmacbtn := widget.NewButton("Randomize WLAN MAC", func() {
|
|
|
|
|
wlanmacfield.SetText(genmac(macprefixfield.Text))
|
|
|
|
|
tabs.SelectIndex(0)
|
|
|
|
|
})
|
|
|
|
|
randombtmacbtn := widget.NewButton("Randomize BT MAC", func() {
|
|
|
|
|
btmacfield.SetText(genmac(macprefixfield.Text))
|
|
|
|
|
tabs.SelectIndex(0)
|
|
|
|
|
})
|
|
|
|
|
atcmdrunbtn := widget.NewButton("Run AT command", func() {
|
|
|
|
|
atcmdres.SetText(suexec(clipath + " atcmd '" + atcmdfield.Text + "'"))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
btnpanel := container.New(layout.NewGridLayout(2), loadbtn, savebtn)
|
|
|
|
|
|
|
|
|
|
/* program about message */
|
|
|
|
|
|
|
|
|
|
aboutmsg := `A universal tool to manipulate device-specific information for Tensor-based Google Pixel smartphones (root access required!)
|
|
|
|
|
|
|
|
|
|
Features:
|
|
|
|
|
|
|
|
|
|
* Edit main PS tags in the devinfo partition
|
|
|
|
|
* Switch the boot mode (normal or factory)
|
|
|
|
|
* Fix IMEI CPSHA after editing devinfo (requires the phone to be booted in the factory mode)
|
|
|
|
|
* Randomize IMEIs and MACs based on TACs or other prefixes
|
|
|
|
|
* Run arbitrary single-line AT commands
|
|
|
|
|
* Fully offline operation, no internet necessary
|
|
|
|
|
|
2025-09-02 15:07:46 +03:00
|
|
|
Works on Pixel 6, 6 Pro, 6a, 7, 7 Pro, 7a, Fold, 8, 8 Pro, 9, 9 Pro, 9 Pro XL, 9 Pro Fold, Pixel 10, Pixel 10 Pro
|
2025-01-01 17:27:12 +02:00
|
|
|
|
|
|
|
|
Get the source code at:
|
|
|
|
|
https://codeberg.org/luxferre/lexipwn
|
|
|
|
|
|
|
|
|
|
Created by Luxferre in 2024-2025, released into public domain with no warranties`
|
|
|
|
|
aboutlbl := widget.NewLabel(aboutmsg)
|
|
|
|
|
aboutlbl.Wrapping = fyne.TextWrapWord
|
|
|
|
|
|
|
|
|
|
/* layout and run */
|
|
|
|
|
|
|
|
|
|
tabs = container.NewAppTabs(
|
|
|
|
|
container.NewTabItem("Devinfo editor", container.NewVScroll(container.New(layout.NewVBoxLayout(),
|
|
|
|
|
widget.NewLabelWithStyle("lexipwn devinfo editor", fyne.TextAlignCenter, fyne.TextStyle{Bold:true}),
|
|
|
|
|
container.New(layout.NewGridLayout(2), bootmodelbl, bootmodefield),
|
|
|
|
|
fieldgrid, layout.NewSpacer(), btnpanel, fixbtn))),
|
|
|
|
|
container.NewTabItem("Utilities", container.NewVScroll(container.New(layout.NewVBoxLayout(),
|
|
|
|
|
widget.NewLabelWithStyle("IMEI/MAC generator", fyne.TextAlignCenter, fyne.TextStyle{Bold:true}),
|
|
|
|
|
container.New(layout.NewGridLayout(2), tacprefixlbl, tacprefixfield,
|
|
|
|
|
macprefixlbl, macprefixfield),
|
|
|
|
|
randomimei1btn, randomimei2btn, randomwlanmacbtn, randombtmacbtn,
|
|
|
|
|
layout.NewSpacer(),
|
|
|
|
|
widget.NewLabelWithStyle("AT command runner", fyne.TextAlignCenter, fyne.TextStyle{Bold:true}),
|
|
|
|
|
widget.NewLabel("Enter AT command (single line)"), atcmdfield, atcmdrunbtn,
|
|
|
|
|
widget.NewLabel("Result"), atcmdres))),
|
|
|
|
|
container.NewTabItem("About", container.NewVScroll(container.New(layout.NewVBoxLayout(),
|
|
|
|
|
layout.NewSpacer(),
|
|
|
|
|
widget.NewLabelWithStyle("About lexipwn", fyne.TextAlignCenter, fyne.TextStyle{Bold:true}),
|
|
|
|
|
aboutlbl,
|
|
|
|
|
layout.NewSpacer()))))
|
|
|
|
|
mainwin.SetContent(tabs)
|
|
|
|
|
|
|
|
|
|
/* emit the click to load devinfo */
|
|
|
|
|
loadbtn.Tapped(nil)
|
|
|
|
|
|
|
|
|
|
mainwin.ShowAndRun()
|
|
|
|
|
}
|