From d51cc716b5d1de25e657fdb89295723b787b6b5f Mon Sep 17 00:00:00 2001 From: Luxferre Date: Wed, 1 Jan 2025 17:27:12 +0200 Subject: [PATCH] initial upload --- .gitignore | 11 + README.md | 114 +++++++ UNLICENSE | 24 ++ lexipwn-cli/Makefile | 6 + lexipwn-cli/go.mod | 3 + lexipwn-cli/lexipwn.go | 341 +++++++++++++++++++++ lexipwn-gui/Icon.png | Bin 0 -> 35012 bytes lexipwn-gui/Makefile | 9 + lexipwn-gui/go.mod | 37 +++ lexipwn-gui/go.sum | 656 +++++++++++++++++++++++++++++++++++++++++ lexipwn-gui/gui.go | 283 ++++++++++++++++++ 11 files changed, 1484 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 UNLICENSE create mode 100644 lexipwn-cli/Makefile create mode 100644 lexipwn-cli/go.mod create mode 100644 lexipwn-cli/lexipwn.go create mode 100644 lexipwn-gui/Icon.png create mode 100644 lexipwn-gui/Makefile create mode 100644 lexipwn-gui/go.mod create mode 100644 lexipwn-gui/go.sum create mode 100644 lexipwn-gui/gui.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dd68d9a --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +.DS_Store +.Trash +*.exe~ +*.dll +*.so +*.dylib +cli-bundle.go +out/ +go.work +go.work.sum +.env diff --git a/README.md b/README.md new file mode 100644 index 0000000..f157bd7 --- /dev/null +++ b/README.md @@ -0,0 +1,114 @@ +# lexipwn: Tensor-based Google Pixel device info Swiss army knife + +The **lexipwn** (pronounced as _lexi-pone_, derived from "pixel" backwards and "pwn") project is a fully free, open-source and universal tool to manipulate device-specific information for Tensor-based Google Pixel smartphones. It comes in two variants: a standalone CLI binary (`lexipwn`) developed in the `lexipwn-cli` subproject and a GUI APK package (`lexipwn.apk`) developed in the `lexipwn-gui` subproject, which has a hard dependency on `lexipwn-cli` (hence shipped in the same repo) and embeds the CLI binary for its own operation. + +Both the CLI and GUI variants are written in Go 1.23. + +**Root access is required to run the tool in either variant!** + +As of now, lexipwn should work on the following Google Pixel models (provided they are rooted): 6, 6 Pro, 6a, 7, 7 Pro, 7a, Fold, 8, 8 Pro, 9, 9 Pro, 9 Pro XL, 9 Pro Fold. + +## 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 + +## Building + +The prebuilt binaries of lexipwn in both variants are available in the "Releases" section of the repo and you can download and use them right away. However, this is a tool designed to run under root privileges, and, for security reasons, it is highly recommended to build your own binaries from the source code whenever possible. + +**Important note: `https://codeberg.org/luxferre/lexipwn.git` is the only original repo of lexipwn, there are no GitHub/GitLab/SourceHut/etc mirrors or other usernames or URLs unless explicitly stated otherwise in this readme and confirmed on the [Luxferre's](https://luxferre.top) web page! If building from a fork, always verify the source code change commits against the original repo to avoid any malware!** + +### CLI variant + +To build the CLI variant of lexipwn, you need to just have Make and Go 1.23 (or above) installed. +Run `make cli` from the repository root and you'll see the static ARM64 binary in the `out/lexipwn` file. + +You can then push the binary to e.g. `/data/local/tmp` directory on the device and directly use it from the `su` shell in ADB or Termux. + +You can also try and build the CLI variant of lexipwn on Termux itself. + +### GUI variant + +To build the GUI variant of lexipwn, you need to have Make, Go 1.23 (or above), [Android NDK](https://github.com/android/ndk/wiki#supported-downloads) and `fyne` command installed. +You can install the [Fyne toolkit](https://fyne.io/) using the following commands: + +``` +go get fyne.io/fyne/v2@latest +go install fyne.io/fyne/v2/cmd/fyne@latest +``` + +Additionally, you need to ensure that the `ANDROID_NDK_HOME` environment variable is pointing to the directory you have extracted the Android NDK to. + +Then, just run `make` from the repository root and you'll see the Android APK package in the `out/lexipwn.apk` file that you can install on your rooted phone. +The very first build may take a rather long time because Fyne will download all missing framework dependencies. +Note that this process will also build the CLI binary, you don't need to do anything with it if you only need the GUI (the binary is embedded into the APK too). + +Afterwards, you can connect your phone via ADB and install the package manually or just run `make install` which will do the same thing. + +## Usage + +### Editing devinfo partition (CLI) + +* List available PS tags (output as `[type] TAB [tagname] TAB [tagvalue]`): `lexipwn listps` +* Change a PS tag type (only `DIUS` and `DIFR` are valid): `lexipwn setpstype [tagname] [DIUS|DIFR]` +* Change PS tag values (string only): `lexipwn setpsval [tagname1] [tagvalue1] [tagname2] [tagvalue2] ...` + +### Editing devinfo partition (GUI) + +The following PS tags are available for editing via the lexipwn GUI: + +* `bootmode` (device boot mode, `normal` or `factory`) +* `imei1` and `imei2` +* `psn` +* `dsn` +* `wlan_mac1` (WLAN MAC address) +* `bt_addr` (Bluetooth MAC address) +* `pcbcfg` (PCB config) +* `devcfg` (Dev config) +* `sku` +* `coo` (country of origin) +* `dpc` +* `wcc` + +On start, lexipwn GUI will read the above tags into corresponding fields of the "Devinfo editor" tab. You can reload the tags at any time by pressing the "Load devinfo" button. After editing the tags, you must press the "Save devinfo" button and confirm the operation. Upon reboot, the phone will read the new tag values. + +### Fixing IMEI CPSHA values after IMEI repair + +If you have altered the IMEI values in devinfo and then rebooted, the device will most probably report 000000000000000 for both IMEI numbers to `*#06#` and to the network. In order to fix this, you need to first boot into the factory mode (set the `bootmode` PS tag to `factory`, save the devinfo partition and reboot the phone), then press the "Fix IMEI CPSHA" button in the lexipwn GUI or run `lexipwn fiximeisha` in the CLI. Then you can (optionally) revert back to the normal boot mode and the new IMEIs will become visible upon reboot. + +### Randomizing IMEIs (CLI) + +Just run `lexipwn genimei [prefix]`. The prefix can be any combination from 0 to 14 digits. Without the prefix, the IMEI will be fully random. You can use the output to substitute into another command, e.g. `lexipwn setpsval imei2 $(lexipwn genimei)` which will fully randomize IMEI2 value. + +### Randomizing IMEIs (GUI) + +In the "Utilities" tab, under the "IMEI/MAC generator" section, enter the prefix (if necessary) into the "IMEI prefix/TAC" field and then tap the "Randomize IMEI 1" or "Randomize IMEI 2" button. The program will substitute the corresponding field in the "Devinfo editor" tab and switch to that tab. Note that this action won't overwrite the devinfo partition, you'll need to tap the "Save devinfo" button to save the result and then perform the CPSHA fix accordingly. + +### Randomizing MAC addresses (GUI only) + +In the "Utilities" tab, under the "IMEI/MAC generator" section, enter the prefix (if necessary) into the "MAC prefix" field (just hex values or separated with colons) and then tap the "Randomize WLAN MAC" or "Randomize BT MAC" button. The program will substitute the corresponding field in the "Devinfo editor" tab and switch to that tab. Note that this action won't overwrite the devinfo partition, you'll need to tap the "Save devinfo" button to save the result. + +### Running AT commands + +You can run simple, single-line AT commands on the Pixel's modem using lexipwn. In the CLI mode, just run `lexipwn atcmd '[your_command]'` and see the result (if it's returned immediately). In the GUI, you can head to the "Utilities" tab, enter the command under the "AT command runner" section, tap the "Run AT command" button and see the result in the text field below. + +## The tech behind lexipwn + +Every Tensor-based Google Pixel phone that exists nowadays (from 6 up to 9 Pro Fold) uses a small, 8192-byte FS-less partition called `devinfo` to store critical device-specific information like serial numbers, IMEIs and primary MAC addresses. Unlike e.g. Qualcomm-based devices, the information in this partition is the source of truth for EFS and other places. The lexipwn project has its roots in an older research that led to creation of the [pxlycan](https://git.luxferre.top/pxlycan/file/pxlycan.tcl.html) tool, which is just an offline devinfo partition editor to use from a PC with Tcl installed. + +The devinfo partition essentially consists of three areas: information header area (first 128 bytes 0-127), PS (protocol station) tag area (from the 128th byte until the half of the file) and PSENV (protocol station environment) tag area (from the 4096th byte until the end of the file). While the pxlycan.tcl tool could edit any of those, lexipwn is only focused on editing the PS tag area, i.e. string tags that can usually be set at the factory only. Moreover, the total set of tags can vary from model to model, so the GUI version only allows you to edit the tags that can be encountered in any of them. + +However, any production-grade Pixel has an IMEI integrity protection mechanism built into its (Exynos-based) modem firmware. An internal SHA256 checksum (based upon the values of both IMEI numbers and probably something else) is stored in the `persist` partition, in the `/mnt/vendor/persist/modem/cpsha` file in its ASCII form. If any of the IMEI values stored in the devinfo partition do not match this hash, the modem will report all zeroes for both IMEIs to the screen and to the network. Early Pixel models like 6 and 6 Pro allowed to bypass this check by lowering the board stage code in the devinfo header area, but this didn't work in the later generations. In order to properly fix this CPSHA file, the calculation request must be made to the modem itself, which can be done via the `AT+GOOGGETIMEISHA` command and manually copying its output to the above path. This command, however, can only work if the phone itself is booted into the factory mode, which in turn can be achieved by modifying the corresponding devinfo PS area tag called `bootmode` by setting it from `normal` to `factory` and a subsequent reboot. + +The last question to clarify is how the AT commands can be run on the Pixel devices. Unfortunately, the only interface that's currently known there is by writing to and reading from the `/dev/umts_router` file, which is not even a proper serial device and cannot be interacted with by the means of `microcom` or other serial terminal software. The AT commands are written one by one to this file, then the responses are read back from it. Not all commands respond immediately though, so sometimes we just don't know when to expect the respnonse. For the purpose of just running `AT+GOOGGETIMEISHA`, lexipwn doesn't implement any response heuristics and only returns the output that it can read immediately, which is fine with this particular AT command. For the sake of completeness, the functionality of running any single-line AT command is also implemented in the program. + +Using all this knowledge, you can now freely create your own devinfo manipulation tool if you don't like lexipwn for any reason. + +## Credits + +Created by Luxferre in 2024-2025, released into public domain with no warranties. diff --git a/UNLICENSE b/UNLICENSE new file mode 100644 index 0000000..68a49da --- /dev/null +++ b/UNLICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/lexipwn-cli/Makefile b/lexipwn-cli/Makefile new file mode 100644 index 0000000..0fd8a84 --- /dev/null +++ b/lexipwn-cli/Makefile @@ -0,0 +1,6 @@ +all: + go mod tidy + GOOS=linux GOARCH=arm64 go build -a -trimpath -gcflags=all="-l -B -e" -ldflags "-s -w" + mkdir -p ../out + mv lexipwn ../out/ + diff --git a/lexipwn-cli/go.mod b/lexipwn-cli/go.mod new file mode 100644 index 0000000..e0a2ee4 --- /dev/null +++ b/lexipwn-cli/go.mod @@ -0,0 +1,3 @@ +module lexipwn + +go 1.23.4 diff --git a/lexipwn-cli/lexipwn.go b/lexipwn-cli/lexipwn.go new file mode 100644 index 0000000..47ee98b --- /dev/null +++ b/lexipwn-cli/lexipwn.go @@ -0,0 +1,341 @@ +/* + lexipwn (CLI version) + + Can do the following on Google Pixel 6 and up to Pixel 9 (requires root access): + + - manipulate devinfo partition (IMEIs, MACs, DSN, PSN, SKU etc) + - fix IMEI SHA (the bootmode PS tag needs to be set to factory) + - generate random but valid IMEIs based on the TAC/prefix + - run arbitrary AT commands on the modem + + Usage: lexipwn [subcommand] [params] + + Available subcommands: + + * listps - list all tags in the PS tag area (TSV format) + * setpsval [name1] [value1] [name2] [value2] ... - set tag values in the PS tag area + * setpstype [name] [DIUS|DIFR] - set a tag type in the PS tag area + * fiximeisha - actualize the IMEI SHA string in the persist partition + (requires to be booted into the factory mode) + * genimei [prefix] - generate a random valid IMEI starting with [prefix] + (doesn't write this IMEI anywhere, so can be used without root) + * atcmd [command] - run a single-line AT command and output its result + + Build with: + + GOOS=linux GOARCH=arm64 go build -a -trimpath -gcflags=all="-l -B -e" -ldflags "-s -w" + (the result will be static; for dynamic PIE aarch64 builds, use GOOS=android instead) + + Created by Luxferre in 2024-2025, released into public domain +*/ + +package main + +import ( + "os" + "bytes" + "encoding/binary" + "fmt" + "io" + "strings" + "strconv" + "math/rand" + "time" +) + +/* a structure to describe a single tag in the PS or PSENV section */ +type DevInfoTag struct { + Type [4]byte /* DIUS or DIFR */ + FullLength uint32 /* name length + value length */ + NameLength uint32 + Name []byte + Value []byte +} + +/* a structure to describe the entire devinfo partition image */ +/* note that the unused areas might actually be used by the device */ +type DevInfoImage struct { + Magic [4]byte /* must be DEVI */ + VerMajor uint16 + VerMinor uint16 + UnusedArea1 [32]byte + BoardProject uint16 + BoardStageCode uint8 + BoardRevHi uint8 + BoardRevLo uint8 + BoardVariant uint8 + SlotARetryCount uint8 + SlotAFlags uint8 + SlotBRetryCount uint8 + SlotBFlags uint8 + UnusedArea2 [74]byte + PStags []DevInfoTag + PSENVtags []DevInfoTag +} + +/* info area byte offsets */ +const offs_SlotARetryCount = 48 +const offs_SlotBRetryCount = 52 +const offs_PStags = 128 +const offs_PSENVtags = 4096 + +/* read the file data into the DevInfoImage structure */ +func readDevInfo(data []byte) DevInfoImage { + r := bytes.NewReader(data) + var info DevInfoImage + /* read the header area */ + binary.Read(r, binary.BigEndian, &info.Magic) + binary.Read(r, binary.LittleEndian, &info.VerMajor) + binary.Read(r, binary.LittleEndian, &info.VerMinor) + binary.Read(r, binary.BigEndian, &info.UnusedArea1) + binary.Read(r, binary.LittleEndian, &info.BoardProject) + binary.Read(r, binary.LittleEndian, &info.BoardStageCode) + binary.Read(r, binary.LittleEndian, &info.BoardRevHi) + binary.Read(r, binary.LittleEndian, &info.BoardRevLo) + binary.Read(r, binary.LittleEndian, &info.BoardVariant) + r.Seek(offs_SlotARetryCount, io.SeekStart) + binary.Read(r, binary.LittleEndian, &info.SlotARetryCount) + binary.Read(r, binary.LittleEndian, &info.SlotAFlags) + r.Seek(offs_SlotBRetryCount, io.SeekStart) + binary.Read(r, binary.LittleEndian, &info.SlotBRetryCount) + binary.Read(r, binary.LittleEndian, &info.SlotBFlags) + binary.Read(r, binary.BigEndian, &info.UnusedArea2) + /* read the PS and PSENV tag areas */ + info.PStags = make([]DevInfoTag, 0) + info.PSENVtags = make([]DevInfoTag, 0) + for { + var tag DevInfoTag + err := binary.Read(r, binary.BigEndian, &tag.Type) + if err != nil { + break + } + binary.Read(r, binary.LittleEndian, &tag.FullLength) + binary.Read(r, binary.LittleEndian, &tag.NameLength) + if tag.FullLength > 0 { + if tag.NameLength > 0 { + tag.Name = make([]byte, tag.NameLength) + binary.Read(r, binary.BigEndian, &tag.Name) + } + tag.Value = make([]byte, tag.FullLength - tag.NameLength) + binary.Read(r, binary.BigEndian, &tag.Value) + } + /* handle edge cases and populate the slices */ + if tag.FullLength < 0xf00 && tag.NameLength > 0 { + curpos, _ := r.Seek(0, io.SeekCurrent) + if curpos < offs_PSENVtags { + info.PStags = append(info.PStags, tag) + } else { + info.PSENVtags = append(info.PSENVtags, tag) + } + } + } + return info +} + +/* write the DevInfoImage structure into 8192 bytes of data */ +func writeDevInfo(info DevInfoImage) []byte { + buf := new(bytes.Buffer) + buf.Grow(8192) + /* write the header area */ + binary.Write(buf, binary.BigEndian, info.Magic) + binary.Write(buf, binary.LittleEndian, info.VerMajor) + binary.Write(buf, binary.LittleEndian, info.VerMinor) + binary.Write(buf, binary.BigEndian, info.UnusedArea1) + binary.Write(buf, binary.LittleEndian, info.BoardProject) + binary.Write(buf, binary.LittleEndian, info.BoardStageCode) + binary.Write(buf, binary.LittleEndian, info.BoardRevHi) + binary.Write(buf, binary.LittleEndian, info.BoardRevLo) + binary.Write(buf, binary.LittleEndian, info.BoardVariant) + binary.Write(buf, binary.LittleEndian, uint16(0)) /* write two zeros */ + binary.Write(buf, binary.LittleEndian, info.SlotARetryCount) + binary.Write(buf, binary.LittleEndian, info.SlotAFlags) + binary.Write(buf, binary.LittleEndian, uint16(0)) /* write two zeros again */ + binary.Write(buf, binary.LittleEndian, info.SlotBRetryCount) + binary.Write(buf, binary.LittleEndian, info.SlotBFlags) + binary.Write(buf, binary.BigEndian, info.UnusedArea2) + /* write the PS tag area */ + var curoffs uint32 = offs_PStags + for _, tag := range info.PStags { + binary.Write(buf, binary.BigEndian, tag.Type) + binary.Write(buf, binary.LittleEndian, tag.FullLength) + binary.Write(buf, binary.LittleEndian, tag.NameLength) + if tag.FullLength > 0 { + if tag.NameLength > 0 { + binary.Write(buf, binary.BigEndian, tag.Name) + } + binary.Write(buf, binary.BigEndian, tag.Value) + } + curoffs += 12 + tag.FullLength + } + /* write the last pseudo-tag of the section */ + binary.Write(buf, binary.BigEndian, []byte("DIFR")) + binary.Write(buf, binary.LittleEndian, uint32(offs_PSENVtags - curoffs - 12)) + /* fill in the zeros before the PSENV section */ + skip := int(offs_PSENVtags - curoffs - 8) + for i:=0; i 0 { + if tag.NameLength > 0 { + binary.Write(buf, binary.BigEndian, tag.Name) + } + binary.Write(buf, binary.BigEndian, tag.Value) + } + curoffs += 12 + tag.FullLength + } + /* write the last pseudo-tag of the section */ + binary.Write(buf, binary.BigEndian, []byte("DIFR")) + skip = int(8184 - curoffs) + binary.Write(buf, binary.LittleEndian, uint32(skip - 4)) + for i:=0; i= 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 +} + +/* run a single AT command and attempt to read its result */ +func runAtCmd(cmd string) string { + ifacefile := "/dev/umts_router" + rcmd := append([]byte(cmd), byte(13), byte(10)) /* add CRLF */ + err := os.WriteFile(ifacefile, rcmd, 0666) /* write the request */ + if err != nil { + panic(err) + } + data, err := os.ReadFile(ifacefile) /* read the response */ + if err != nil { + panic(err) + } + return string(data) +} + +/* entry point */ +func main() { + params := os.Args[1:] + subcmd := params[0] + fname := "/dev/block/bootdevice/by-name/devinfo" + var info DevInfoImage + /* preread the data if the subcommand is devinfo-related */ + if subcmd == "listps" || subcmd == "setpsval" || subcmd == "setpstype" { + data, err := os.ReadFile(fname) + if err != nil { + panic(err) + } + info = readDevInfo(data) + } + /* execute the subcommand */ + switch subcmd { + case "listps": + for _, tag := range info.PStags { + fmt.Printf("%s\t%s\t%s\n", tag.Type, tag.Name, tag.Value) + } + case "setpsval": + valpairs := params[1:] + plen := len(valpairs) + if plen % 2 == 1 { + fmt.Println("invalid parameter count!") + return + } + for i := 0; i < plen; i+=2 { + tagidx := findTag(info.PStags, valpairs[i]) + if tagidx > -1 { + setStringTag(&info.PStags[tagidx], valpairs[i+1]) + } + } + os.WriteFile(fname, writeDevInfo(info), 0644) + fmt.Println("devinfo image saved") + case "setpstype": + if len(params) != 3 { + println("invalid parameter count!") + return + } + tagtype := params[2] + tagidx := findTag(info.PStags, params[1]) + if tagidx > -1 { + if tagtype != "DIFR" { /* only two types are allowed, force DIUS unless it's DIFR */ + tagtype = "DIUS" + } + copy(info.PStags[tagidx].Type[:], []byte(tagtype)) + os.WriteFile(fname, writeDevInfo(info), 0644) + fmt.Println("devinfo image saved") + } + case "genimei": + rand.Seed(time.Now().UTC().UnixNano()) + var imeisb strings.Builder + imeisb.WriteString(params[1]) /* 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 */ + fmt.Printf("%s", imei + strconv.Itoa(calcLuhn(imei))) + case "atcmd": + if len(params) != 2 { + println("invalid parameter count!") + return + } + fmt.Printf("%s\n", runAtCmd(params[1])) + case "fiximeisha": + rawsharesult := runAtCmd("AT+GOOGGETIMEISHA") + sharesult := strings.Split(strings.ReplaceAll(rawsharesult, "\r\n", "\n"), "\n") + if len(sharesult) > 2 { + if sharesult[2] == "ERROR" { + fmt.Println("Error getting IMEI SHA, are you booted into the factory mode?") + } else { + shaparts := strings.Split(sharesult[2], ":") + if len(shaparts) > 1 { + targetfile := "/mnt/vendor/persist/modem/cpsha" + os.WriteFile(targetfile, []byte(shaparts[1]), 0644) + fmt.Println("CPSHA file written") + } + } + } + default: + fmt.Println("Valid subcommands: listps, setpsval, setpstype, fiximeisha, genimei, atcmd") + } +} + diff --git a/lexipwn-gui/Icon.png b/lexipwn-gui/Icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9263daf8ff23de04b953c148931b34d1a8677ec0 GIT binary patch literal 35012 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 erKP2HyYycoEu@i!V433p0000= 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 + +Works on Pixel 6, 6 Pro, 6a, 7, 7 Pro, 7a, Fold, 8, 8 Pro, 9, 9 Pro, 9 Pro XL, 9 Pro Fold + +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() +}