Upload files to "/"

This commit is contained in:
NJL
2026-07-12 19:37:19 +00:00
parent a1d1b7cfad
commit 3374e16cc0
10 changed files with 1720 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "apk"
version = "0.1.0"
edition = "2021"
[[bin]]
name = "apk"
path = "src/main.rs"
[profile.release]
opt-level = "z"
lto = true
panic = "abort"
+278
View File
@@ -0,0 +1,278 @@
const RES_XML_TYPE: u16 = 0x0003;
const RES_STRING_POOL_TYPE: u16 = 0x0001;
const RES_XML_RESOURCE_MAP_TYPE: u16 = 0x0180;
const RES_XML_START_NAMESPACE_TYPE: u16 = 0x0100;
const RES_XML_END_NAMESPACE_TYPE: u16 = 0x0101;
const RES_XML_START_ELEMENT_TYPE: u16 = 0x0102;
const RES_XML_END_ELEMENT_TYPE: u16 = 0x0103;
const NO_COMMENT: u32 = 0;
const ATTR_RES_IDS: &[(&str, u32)] = &[
("versionCode", 0x0101021b),
("versionName", 0x0101021c),
("minSdkVersion", 0x0101020c),
("targetSdkVersion", 0x01010270),
("label", 0x01010001),
("name", 0x01010003),
("exported", 0x01010010),
("hasCode", 0x0101000c),
("configChanges", 0x0101001f),
];
pub const TYPE_STRING: u8 = 0x03;
pub const TYPE_INT_DEC: u8 = 0x10;
pub const TYPE_INT_BOOL: u8 = 0x12;
struct StringPool {
strings: Vec<String>,
index: std::collections::HashMap<String, u32>,
}
impl StringPool {
fn new() -> Self {
StringPool {
strings: Vec::new(),
index: std::collections::HashMap::new(),
}
}
fn intern(&mut self, s: &str) -> u32 {
if let Some(&i) = self.index.get(s) {
return i;
}
let i = self.strings.len() as u32;
self.strings.push(s.to_string());
self.index.insert(s.to_string(), i);
i
}
fn encode(&self) -> Vec<u8> {
const UTF8_FLAG: u32 = 1 << 8;
let mut offsets = Vec::new();
let mut string_data = Vec::new();
for s in &self.strings {
offsets.push(string_data.len() as u32);
let utf16_units = s.encode_utf16().count();
let bytes = s.as_bytes();
push_utf8_len(&mut string_data, utf16_units);
push_utf8_len(&mut string_data, bytes.len());
string_data.extend_from_slice(bytes);
string_data.push(0);
}
while string_data.len() % 4 != 0 {
string_data.push(0);
}
let string_count = self.strings.len() as u32;
let header_size = 28u32;
let offsets_size = string_count * 4;
let strings_start = header_size + offsets_size;
let chunk_size = strings_start + string_data.len() as u32;
let mut out = Vec::new();
push_u16(&mut out, RES_STRING_POOL_TYPE);
push_u16(&mut out, 28);
push_u32(&mut out, chunk_size);
push_u32(&mut out, string_count);
push_u32(&mut out, 0);
push_u32(&mut out, UTF8_FLAG);
push_u32(&mut out, strings_start);
push_u32(&mut out, 0);
for o in offsets {
push_u32(&mut out, o);
}
out.extend_from_slice(&string_data);
out
}
}
struct Attr {
ns: Option<u32>,
name: u32,
raw_value: i32,
typ: u8,
data: u32,
}
pub struct AxmlBuilder {
pool: StringPool,
body: Vec<u8>,
android_ns_uri: u32,
android_ns_prefix: u32,
line: u32,
}
impl AxmlBuilder {
pub fn new() -> Self {
AxmlBuilder {
pool: StringPool::new(),
body: Vec::new(),
android_ns_uri: 0xFFFF_FFFF,
android_ns_prefix: 0xFFFF_FFFF,
line: 1,
}
}
fn ns_uri(&mut self) -> u32 {
if self.android_ns_uri == 0xFFFF_FFFF {
self.android_ns_uri = self.pool.intern("http://schemas.android.com/apk/res/android");
}
self.android_ns_uri
}
pub fn intern(&mut self, s: &str) -> u32 {
self.pool.intern(s)
}
pub const EMIT_NAMESPACE_CHUNKS: bool = false;
pub fn start(&mut self) {
if !Self::EMIT_NAMESPACE_CHUNKS {
return;
}
let mut c = Vec::new();
push_u16(&mut c, RES_XML_START_NAMESPACE_TYPE);
push_u16(&mut c, 16);
push_u32(&mut c, 24);
push_u32(&mut c, self.line);
push_u32(&mut c, NO_COMMENT);
push_u32(&mut c, self.android_ns_prefix);
push_u32(&mut c, self.android_ns_uri);
self.body.extend_from_slice(&c);
}
pub fn end(&mut self) {
if !Self::EMIT_NAMESPACE_CHUNKS {
return;
}
let mut c = Vec::new();
push_u16(&mut c, RES_XML_END_NAMESPACE_TYPE);
push_u16(&mut c, 16);
push_u32(&mut c, 24);
push_u32(&mut c, self.line);
push_u32(&mut c, 0xFFFF_FFFF);
push_u32(&mut c, self.android_ns_prefix);
push_u32(&mut c, self.android_ns_uri);
self.body.extend_from_slice(&c);
}
pub fn open(&mut self, tag: u32, attrs: &[(u32, u8, u32, i32)]) {
let with_ns: Vec<(u32, u8, u32, i32, bool)> =
attrs.iter().map(|&(n, t, d, r)| (n, t, d, r, true)).collect();
self.open_ns(tag, &with_ns);
}
pub fn open_ns(&mut self, tag: u32, attrs: &[(u32, u8, u32, i32, bool)]) {
let uri = if attrs.iter().any(|a| a.4) { Some(self.ns_uri()) } else { None };
let attr_structs: Vec<Attr> = attrs
.iter()
.map(|&(name, typ, data, raw, android_ns)| Attr {
ns: if android_ns { uri } else { None },
name,
raw_value: raw,
typ,
data,
})
.collect();
let mut c = Vec::new();
let attr_count = attr_structs.len() as u32;
let chunk_size = 16 + 20 + attr_count * 20;
push_u16(&mut c, RES_XML_START_ELEMENT_TYPE);
push_u16(&mut c, 16);
push_u32(&mut c, chunk_size);
push_u32(&mut c, self.line);
push_u32(&mut c, NO_COMMENT);
push_u32(&mut c, 0);
push_u32(&mut c, tag);
push_u16(&mut c, 20);
push_u16(&mut c, 20);
push_u16(&mut c, attr_count as u16);
push_u16(&mut c, 0);
push_u16(&mut c, 0);
push_u16(&mut c, 0);
for a in &attr_structs {
push_u32(&mut c, a.ns.unwrap_or(0xFFFF_FFFF));
push_u32(&mut c, a.name);
push_u32(&mut c, a.raw_value as u32);
push_u16(&mut c, 8);
c.push(0);
c.push(a.typ);
push_u32(&mut c, a.data);
}
self.body.extend_from_slice(&c);
}
pub fn close(&mut self, tag: u32) {
let mut c = Vec::new();
push_u16(&mut c, RES_XML_END_ELEMENT_TYPE);
push_u16(&mut c, 16);
push_u32(&mut c, 24);
push_u32(&mut c, self.line);
push_u32(&mut c, NO_COMMENT);
push_u32(&mut c, 0);
push_u32(&mut c, tag);
self.body.extend_from_slice(&c);
}
pub fn res_id_for(name: &str) -> Option<u32> {
ATTR_RES_IDS.iter().find(|(n, _)| *n == name).map(|(_, id)| *id)
}
pub fn finish(self, attr_name_to_resid: &[(u32, u32)]) -> Vec<u8> {
let pool_chunk = self.pool.encode();
let max_attr_idx = attr_name_to_resid
.iter()
.map(|&(str_idx, _)| str_idx as usize)
.max()
.unwrap_or(0);
let mut resmap = vec![0u32; max_attr_idx + 1];
for &(str_idx, resid) in attr_name_to_resid {
if (str_idx as usize) < resmap.len() {
resmap[str_idx as usize] = resid;
}
}
let mut resmap_chunk = Vec::new();
let resmap_size = 8 + resmap.len() as u32 * 4;
push_u16(&mut resmap_chunk, RES_XML_RESOURCE_MAP_TYPE);
push_u16(&mut resmap_chunk, 8);
push_u32(&mut resmap_chunk, resmap_size);
for r in &resmap {
push_u32(&mut resmap_chunk, *r);
}
let inner_len = pool_chunk.len() + resmap_chunk.len() + self.body.len();
let total = 8 + inner_len;
let mut out = Vec::new();
push_u16(&mut out, RES_XML_TYPE);
push_u16(&mut out, 8);
push_u32(&mut out, total as u32);
out.extend_from_slice(&pool_chunk);
out.extend_from_slice(&resmap_chunk);
out.extend_from_slice(&self.body);
out
}
}
pub mod val {
#[allow(unused_imports)]
pub use super::{TYPE_INT_BOOL, TYPE_INT_DEC, TYPE_STRING};
}
fn push_u16(v: &mut Vec<u8>, x: u16) {
v.extend_from_slice(&x.to_le_bytes());
}
fn push_u32(v: &mut Vec<u8>, x: u32) {
v.extend_from_slice(&x.to_le_bytes());
}
fn push_utf8_len(v: &mut Vec<u8>, len: usize) {
if len < 0x80 {
v.push(len as u8);
} else {
v.push(((len >> 8) & 0x7f) as u8 | 0x80);
v.push((len & 0xff) as u8);
}
}
+208
View File
@@ -0,0 +1,208 @@
#[derive(Clone, Debug)]
pub struct BigUint {
limbs: Vec<u32>,
}
impl BigUint {
pub fn zero() -> Self {
BigUint { limbs: vec![] }
}
pub fn from_u32(x: u32) -> Self {
if x == 0 {
BigUint::zero()
} else {
BigUint { limbs: vec![x] }
}
}
pub fn from_be_bytes(bytes: &[u8]) -> Self {
let mut b = bytes.to_vec();
while b.len() > 1 && b[0] == 0 {
b.remove(0);
}
let mut limbs = Vec::new();
let mut i = b.len();
while i > 0 {
let start = i.saturating_sub(4);
let mut limb = 0u32;
for (k, &byte) in b[start..i].iter().enumerate() {
let shift = (b[start..i].len() - 1 - k) * 8;
limb |= (byte as u32) << shift;
}
limbs.push(limb);
i = start;
}
let mut r = BigUint { limbs };
r.normalize();
r
}
pub fn to_be_bytes(&self) -> Vec<u8> {
if self.limbs.is_empty() {
return vec![0];
}
let mut out = Vec::new();
for &limb in self.limbs.iter().rev() {
out.extend_from_slice(&limb.to_be_bytes());
}
while out.len() > 1 && out[0] == 0 {
out.remove(0);
}
out
}
pub fn to_be_bytes_padded(&self, len: usize) -> Vec<u8> {
let mut b = self.to_be_bytes();
if b.len() > len {
b = b[b.len() - len..].to_vec();
}
while b.len() < len {
b.insert(0, 0);
}
b
}
fn normalize(&mut self) {
while let Some(&0) = self.limbs.last() {
self.limbs.pop();
}
}
pub fn is_zero(&self) -> bool {
self.limbs.is_empty()
}
fn bit_len(&self) -> usize {
if self.limbs.is_empty() {
return 0;
}
let top = *self.limbs.last().unwrap();
(self.limbs.len() - 1) * 32 + (32 - top.leading_zeros() as usize)
}
fn bit(&self, i: usize) -> bool {
let limb = i / 32;
let off = i % 32;
if limb >= self.limbs.len() {
return false;
}
(self.limbs[limb] >> off) & 1 == 1
}
fn cmp(&self, other: &BigUint) -> std::cmp::Ordering {
use std::cmp::Ordering::*;
if self.limbs.len() != other.limbs.len() {
return self.limbs.len().cmp(&other.limbs.len());
}
for i in (0..self.limbs.len()).rev() {
match self.limbs[i].cmp(&other.limbs[i]) {
Equal => continue,
ord => return ord,
}
}
Equal
}
fn sub(&self, other: &BigUint) -> BigUint {
let mut out = Vec::new();
let mut borrow = 0i64;
for i in 0..self.limbs.len() {
let a = self.limbs[i] as i64;
let b = *other.limbs.get(i).unwrap_or(&0) as i64;
let mut diff = a - b - borrow;
if diff < 0 {
diff += 1 << 32;
borrow = 1;
} else {
borrow = 0;
}
out.push(diff as u32);
}
let mut r = BigUint { limbs: out };
r.normalize();
r
}
fn shl1(&self) -> BigUint {
let mut out = Vec::with_capacity(self.limbs.len() + 1);
let mut carry = 0u32;
for &limb in &self.limbs {
let new = (limb << 1) | carry;
carry = limb >> 31;
out.push(new);
}
if carry != 0 {
out.push(carry);
}
let mut r = BigUint { limbs: out };
r.normalize();
r
}
fn mul(&self, other: &BigUint) -> BigUint {
if self.is_zero() || other.is_zero() {
return BigUint::zero();
}
let mut out = vec![0u64; self.limbs.len() + other.limbs.len()];
for (i, &a) in self.limbs.iter().enumerate() {
let mut carry = 0u64;
for (j, &b) in other.limbs.iter().enumerate() {
let cur = out[i + j] + (a as u64) * (b as u64) + carry;
out[i + j] = cur & 0xFFFF_FFFF;
carry = cur >> 32;
}
out[i + other.limbs.len()] += carry;
}
let limbs: Vec<u32> = out.iter().map(|&x| x as u32).collect();
let mut r = BigUint { limbs };
r.normalize();
r
}
fn rem(&self, m: &BigUint) -> BigUint {
use std::cmp::Ordering::*;
if m.is_zero() {
panic!("mod by zero");
}
if self.cmp(m) == Less {
return self.clone();
}
let mut rem = BigUint::zero();
for i in (0..self.bit_len()).rev() {
rem = rem.shl1();
if self.bit(i) {
if rem.limbs.is_empty() {
rem.limbs.push(1);
} else {
rem.limbs[0] |= 1;
}
}
if rem.cmp(m) != Less {
rem = rem.sub(m);
}
}
rem.normalize();
rem
}
fn mulmod(&self, other: &BigUint, m: &BigUint) -> BigUint {
self.mul(other).rem(m)
}
pub fn modpow(&self, exp: &BigUint, m: &BigUint) -> BigUint {
if m.cmp(&BigUint::from_u32(1)) == std::cmp::Ordering::Equal {
return BigUint::zero();
}
let mut result = BigUint::from_u32(1);
let mut base = self.rem(m);
let bits = exp.bit_len();
for i in 0..bits {
if exp.bit(i) {
result = result.mulmod(&base, m);
}
base = base.mulmod(&base, m);
}
result
}
}
+63
View File
@@ -0,0 +1,63 @@
use crate::keydata::SPKI_DER;
pub fn build_min_cert() -> Vec<u8> {
let sig_alg = der_seq(&der_oid(&[0x2a]));
let name = der_seq(&[]);
let not_before = der_tlv(0x17, b"000101000000Z");
let not_after = der_tlv(0x17, b"490101000000Z");
let validity = der_seq(&[not_before, not_after].concat());
let cert_spki = SPKI_DER.to_vec();
let tbs = der_seq(
&[
der_int(&[1]),
sig_alg.clone(),
name.clone(),
validity,
name,
cert_spki,
]
.concat(),
);
let sig_bitstring = der_tlv(0x03, &[0x00]);
der_seq(&[tbs, sig_alg, sig_bitstring].concat())
}
fn der_len(len: usize) -> Vec<u8> {
if len < 0x80 {
vec![len as u8]
} else {
let mut b = Vec::new();
let mut l = len;
while l > 0 {
b.insert(0, (l & 0xff) as u8);
l >>= 8;
}
let mut out = vec![0x80 | b.len() as u8];
out.extend_from_slice(&b);
out
}
}
fn der_tlv(tag: u8, body: &[u8]) -> Vec<u8> {
let mut out = vec![tag];
out.extend_from_slice(&der_len(body.len()));
out.extend_from_slice(body);
out
}
fn der_seq(body: &[u8]) -> Vec<u8> {
der_tlv(0x30, body)
}
fn der_oid(bytes: &[u8]) -> Vec<u8> {
der_tlv(0x06, bytes)
}
fn der_int(bytes: &[u8]) -> Vec<u8> {
let mut b = bytes.to_vec();
if !b.is_empty() && b[0] & 0x80 != 0 {
b.insert(0, 0x00);
}
der_tlv(0x02, &b)
}
+657
View File
@@ -0,0 +1,657 @@
enum Token {
Lit(u8),
Match { len: u16, dist: u16 },
}
pub fn deflate(input: &[u8]) -> Vec<u8> {
let mut best: Option<Vec<u8>> = None;
let consider = |block: Vec<u8>, best: &mut Option<Vec<u8>>| {
if best.as_ref().map_or(true, |b| block.len() < b.len()) {
*best = Some(block);
}
};
let lazy = lz77(input);
consider(emit_dynamic_block(&lazy), &mut best);
consider(emit_fixed_block(&lazy), &mut best);
let matches = build_match_table(input);
let default_parse = lz77_optimal(input, &matches, &default_costs());
consider(emit_dynamic_block(&default_parse), &mut best);
consider(emit_fixed_block(&default_parse), &mut best);
for seed in [&default_parse, &lazy] {
let mut last = lz77_optimal(input, &matches, &costs_from_tokens(seed));
for _ in 0..30 {
consider(emit_dynamic_block(&last), &mut best);
consider(emit_fixed_block(&last), &mut best);
let next = lz77_optimal(input, &matches, &costs_from_tokens(&last));
last = next;
}
}
best.unwrap()
}
struct Costs {
lit: [u32; 288],
dist: [u32; 30],
}
fn default_costs() -> Costs {
let mut lit = [9u32; 288];
for i in 0..144 {
lit[i] = 8;
}
for i in 256..288 {
lit[i] = 8;
}
Costs { lit, dist: [5u32; 30] }
}
fn costs_from_tokens(tokens: &[Token]) -> Costs {
let (lit_freq, dist_freq) = tally(tokens);
let lit_lens = huffman_lengths(&lit_freq, 15);
let dist_lens = huffman_lengths(&dist_freq, 15);
let mut lit = [0u32; 288];
for i in 0..288 {
lit[i] = if lit_lens[i] > 0 { lit_lens[i] as u32 } else { 15 };
}
let mut dist = [0u32; 30];
for i in 0..30 {
dist[i] = if dist_lens[i] > 0 { dist_lens[i] as u32 } else { 15 };
}
Costs { lit, dist }
}
fn build_match_table(input: &[u8]) -> (Vec<u16>, Vec<u16>) {
let n = input.len();
let mut best_len = vec![0u16; n];
let mut best_dist = vec![0u16; n];
if n == 0 {
return (best_len, best_dist);
}
let mut head = vec![usize::MAX; 1 << 15];
let mut prev = vec![usize::MAX; n];
let hash = |a: u8, b: u8, c: u8| -> usize {
(((a as usize) << 10) ^ ((b as usize) << 5) ^ (c as usize)) & ((1 << 15) - 1)
};
for i in 0..n {
if i + 3 <= n {
let h = hash(input[i], input[i + 1], input[i + 2]);
let mut j = head[h];
let mut chain = 4096;
let max_len = (n - i).min(258);
let mut bl = 0;
let mut bd = 0;
while j != usize::MAX && chain > 0 {
if i - j > 32768 {
break;
}
let mut l = 0;
while l < max_len && input[j + l] == input[i + l] {
l += 1;
}
if l > bl {
bl = l;
bd = i - j;
if l >= max_len {
break;
}
}
j = prev[j];
chain -= 1;
}
best_len[i] = bl as u16;
best_dist[i] = bd as u16;
prev[i] = head[h];
head[h] = i;
}
}
(best_len, best_dist)
}
fn lz77_optimal(input: &[u8], matches: &(Vec<u16>, Vec<u16>), costs: &Costs) -> Vec<Token> {
let n = input.len();
if n == 0 {
return Vec::new();
}
let (best_len, best_dist) = matches;
let lit_cost = |b: u8| -> u32 { costs.lit[b as usize] };
let match_cost = |len: u16, dist: u16| -> u32 {
let (lsym, _, lex) = length_code(len);
let (dsym, _, dex) = dist_code(dist);
costs.lit[lsym as usize] + lex + costs.dist[dsym as usize] + dex
};
let mut cost = vec![u32::MAX; n + 1];
let mut choice_len = vec![0u16; n + 1];
cost[n] = 0;
for i in (0..n).rev() {
let mut bc = lit_cost(input[i]).saturating_add(cost[i + 1]);
let mut bl = 0u16;
let ml = best_len[i];
if ml >= 3 {
let d = best_dist[i];
let mut l = 3u16;
while l <= ml {
let nxt = cost[i + l as usize];
if nxt != u32::MAX {
let c = match_cost(l, d).saturating_add(nxt);
if c < bc {
bc = c;
bl = l;
}
}
l += 1;
}
}
cost[i] = bc;
choice_len[i] = bl;
}
let mut tokens = Vec::new();
let mut i = 0;
while i < n {
let l = choice_len[i];
if l >= 3 {
tokens.push(Token::Match { len: l, dist: best_dist[i] });
i += l as usize;
} else {
tokens.push(Token::Lit(input[i]));
i += 1;
}
}
tokens
}
fn lz77(input: &[u8]) -> Vec<Token> {
let n = input.len();
let mut tokens = Vec::new();
let mut head = vec![usize::MAX; 1 << 15];
let mut prev = vec![usize::MAX; n.max(1)];
let hash = |a: u8, b: u8, c: u8| -> usize {
(((a as usize) << 10) ^ ((b as usize) << 5) ^ (c as usize)) & ((1 << 15) - 1)
};
let find = |p: usize, head: &[usize], prev: &[usize]| -> (usize, usize) {
if p + 3 > n {
return (0, 0);
}
let h = hash(input[p], input[p + 1], input[p + 2]);
let mut j = head[h];
let mut best_len = 0;
let mut best_dist = 0;
let mut chain = 4096;
let max_len = (n - p).min(258);
while j != usize::MAX && chain > 0 {
if p - j > 32768 {
break;
}
let mut l = 0;
while l < max_len && input[j + l] == input[p + l] {
l += 1;
}
if l > best_len {
best_len = l;
best_dist = p - j;
if l >= max_len {
break;
}
}
j = prev[j];
chain -= 1;
}
(best_len, best_dist)
};
let insert = |p: usize, head: &mut Vec<usize>, prev: &mut Vec<usize>| {
if p + 3 <= n {
let h = hash(input[p], input[p + 1], input[p + 2]);
prev[p] = head[h];
head[h] = p;
}
};
let mut i = 0;
let mut inserted = 0usize;
macro_rules! insert_before {
($before:expr) => {{
let before = $before;
while inserted < before && inserted < n {
insert(inserted, &mut head, &mut prev);
inserted += 1;
}
}};
}
while i < n {
insert_before!(i);
let (mlen, mdist) = find(i, &head, &prev);
if mlen >= 3 {
if i + 1 < n {
insert_before!(i + 1);
let (next_len, _) = find(i + 1, &head, &prev);
if next_len > mlen {
tokens.push(Token::Lit(input[i]));
i += 1;
continue;
}
}
tokens.push(Token::Match { len: mlen as u16, dist: mdist as u16 });
i += mlen;
} else {
tokens.push(Token::Lit(input[i]));
i += 1;
}
}
tokens
}
fn length_code(len: u16) -> (u16, u32, u32) {
const BASE: [(u16, u16, u32); 29] = [
(257, 3, 0), (258, 4, 0), (259, 5, 0), (260, 6, 0), (261, 7, 0),
(262, 8, 0), (263, 9, 0), (264, 10, 0), (265, 11, 1), (266, 13, 1),
(267, 15, 1), (268, 17, 1), (269, 19, 2), (270, 23, 2), (271, 27, 2),
(272, 31, 2), (273, 35, 3), (274, 43, 3), (275, 51, 3), (276, 59, 3),
(277, 67, 4), (278, 83, 4), (279, 99, 4), (280, 115, 4), (281, 131, 5),
(282, 163, 5), (283, 195, 5), (284, 227, 5), (285, 258, 0),
];
let mut chosen = BASE[0];
for &e in BASE.iter() {
if e.1 <= len {
chosen = e;
} else {
break;
}
}
let (sym, base, ebits) = chosen;
(sym, (len - base) as u32, ebits)
}
fn dist_code(dist: u16) -> (u16, u32, u32) {
const BASE: [(u16, u16, u32); 30] = [
(0, 1, 0), (1, 2, 0), (2, 3, 0), (3, 4, 0), (4, 5, 1), (5, 7, 1),
(6, 9, 2), (7, 13, 2), (8, 17, 3), (9, 25, 3), (10, 33, 4), (11, 49, 4),
(12, 65, 5), (13, 97, 5), (14, 129, 6), (15, 193, 6), (16, 257, 7),
(17, 385, 7), (18, 513, 8), (19, 769, 8), (20, 1025, 9), (21, 1537, 9),
(22, 2049, 10), (23, 3073, 10), (24, 4097, 11), (25, 6145, 11),
(26, 8193, 12), (27, 12289, 12), (28, 16385, 13), (29, 24577, 13),
];
let mut chosen = BASE[0];
for &e in BASE.iter() {
if e.1 <= dist {
chosen = e;
} else {
break;
}
}
let (sym, base, ebits) = chosen;
(sym, (dist - base) as u32, ebits)
}
fn length_extra_bits(sym: u16) -> u32 {
length_code_extra(sym)
}
fn length_code_extra(sym: u16) -> u32 {
match sym {
257..=264 => 0,
265..=268 => 1,
269..=272 => 2,
273..=276 => 3,
277..=280 => 4,
281..=284 => 5,
285 => 0,
_ => 0,
}
}
fn dist_extra_bits(sym: u16) -> u32 {
match sym {
0..=3 => 0,
s => ((s / 2) - 1) as u32,
}
}
fn huffman_lengths(freqs: &[u32], max_bits: usize) -> Vec<u8> {
let n = freqs.len();
let mut lens = vec![0u8; n];
let used: Vec<usize> = (0..n).filter(|&i| freqs[i] > 0).collect();
if used.is_empty() {
return lens;
}
if used.len() == 1 {
lens[used[0]] = 1;
return lens;
}
#[derive(Clone)]
struct Node {
freq: u64,
sym: i32,
left: i32,
right: i32,
}
let mut nodes: Vec<Node> = Vec::new();
let mut heap: Vec<usize> = Vec::new();
for &s in &used {
nodes.push(Node { freq: freqs[s] as u64, sym: s as i32, left: -1, right: -1 });
heap.push(nodes.len() - 1);
}
let pop_min = |heap: &mut Vec<usize>, nodes: &Vec<Node>| -> usize {
let mut mi = 0;
for k in 1..heap.len() {
if nodes[heap[k]].freq < nodes[heap[mi]].freq {
mi = k;
}
}
heap.swap_remove(mi)
};
while heap.len() > 1 {
let a = pop_min(&mut heap, &nodes);
let b = pop_min(&mut heap, &nodes);
let f = nodes[a].freq + nodes[b].freq;
nodes.push(Node { freq: f, sym: -1, left: a as i32, right: b as i32 });
heap.push(nodes.len() - 1);
}
let root = heap[0];
fn depth(nodes: &[Node], idx: i32, d: u8, lens: &mut Vec<u8>) {
let node = &nodes[idx as usize];
if node.sym >= 0 {
lens[node.sym as usize] = d.max(1);
} else {
depth(nodes, node.left, d + 1, lens);
depth(nodes, node.right, d + 1, lens);
}
}
depth(&nodes, root as i32, 0, &mut lens);
clamp_lengths(&mut lens, max_bits);
lens
}
fn clamp_lengths(lens: &mut [u8], max_bits: usize) {
let maxb = max_bits as u8;
loop {
let mut over = false;
for l in lens.iter() {
if *l > maxb {
over = true;
break;
}
}
if !over {
break;
}
let mut long_idx = None;
for (i, l) in lens.iter().enumerate() {
if *l > maxb {
long_idx = Some(i);
break;
}
}
let li = long_idx.unwrap();
let mut donor = None;
for (i, l) in lens.iter().enumerate() {
if *l > 0 && *l < maxb && i != li {
donor = Some(i);
break;
}
}
match donor {
Some(di) => {
lens[li] = maxb;
lens[di] += 1;
}
None => {
lens[li] = maxb;
}
}
}
}
fn canonical_codes(lens: &[u8]) -> Vec<(u32, u32)> {
let max_len = *lens.iter().max().unwrap_or(&0) as usize;
let mut bl_count = vec![0u32; max_len + 1];
for &l in lens {
if l > 0 {
bl_count[l as usize] += 1;
}
}
let mut next_code = vec![0u32; max_len + 2];
let mut code = 0u32;
for bits in 1..=max_len {
code = (code + bl_count[bits - 1]) << 1;
next_code[bits] = code;
}
let mut codes = vec![(0u32, 0u32); lens.len()];
for (i, &l) in lens.iter().enumerate() {
if l > 0 {
codes[i] = (next_code[l as usize], l as u32);
next_code[l as usize] += 1;
}
}
codes
}
fn tally(tokens: &[Token]) -> ([u32; 288], [u32; 30]) {
let mut lit = [0u32; 288];
let mut dst = [0u32; 30];
for t in tokens {
match t {
Token::Lit(b) => lit[*b as usize] += 1,
Token::Match { len, dist } => {
let (lc, _, _) = length_code(*len);
lit[lc as usize] += 1;
let (dc, _, _) = dist_code(*dist);
dst[dc as usize] += 1;
}
}
}
lit[256] += 1;
(lit, dst)
}
fn emit_dynamic_block(tokens: &[Token]) -> Vec<u8> {
let (lit_freq, dist_freq) = tally(tokens);
let mut lit_lens = huffman_lengths(&lit_freq, 15);
let mut dist_lens = huffman_lengths(&dist_freq, 15);
let mut hlit = 286;
while hlit > 257 && lit_lens[hlit - 1] == 0 {
hlit -= 1;
}
let mut hdist = 30;
while hdist > 1 && dist_lens[hdist - 1] == 0 {
hdist -= 1;
}
lit_lens.truncate(hlit);
dist_lens.truncate(hdist);
let lit_codes = canonical_codes(&lit_lens);
let dist_codes = canonical_codes(&dist_lens);
let mut cl_seq: Vec<u8> = Vec::new();
cl_seq.extend_from_slice(&lit_lens);
cl_seq.extend_from_slice(&dist_lens);
let (cl_syms, cl_extra) = rle_code_lengths(&cl_seq);
let mut cl_freq = [0u32; 19];
for &s in &cl_syms {
cl_freq[s as usize] += 1;
}
let cl_lens_full = huffman_lengths(&cl_freq, 7);
let cl_codes = canonical_codes(&cl_lens_full);
const ORDER: [usize; 19] = [
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15,
];
let mut hclen = 19;
while hclen > 4 && cl_lens_full[ORDER[hclen - 1]] == 0 {
hclen -= 1;
}
let mut bw = BitWriter::new();
bw.write_bits(1, 1);
bw.write_bits(0b10, 2);
bw.write_bits((hlit - 257) as u32, 5);
bw.write_bits((hdist - 1) as u32, 5);
bw.write_bits((hclen - 4) as u32, 4);
for k in 0..hclen {
bw.write_bits(cl_lens_full[ORDER[k]] as u32, 3);
}
for (idx, &s) in cl_syms.iter().enumerate() {
let (code, len) = cl_codes[s as usize];
bw.write_bits_msb(code, len);
match s {
16 => bw.write_bits(cl_extra[idx], 2),
17 => bw.write_bits(cl_extra[idx], 3),
18 => bw.write_bits(cl_extra[idx], 7),
_ => {}
}
}
emit_tokens(&mut bw, tokens, &lit_codes, &dist_codes);
bw.finish()
}
fn emit_fixed_block(tokens: &[Token]) -> Vec<u8> {
let mut bw = BitWriter::new();
bw.write_bits(1, 1);
bw.write_bits(0b01, 2);
let lit_codes: Vec<(u32, u32)> = (0..288).map(fixed_lit_code_u).collect();
let dist_codes: Vec<(u32, u32)> = (0..30).map(|s| (s as u32, 5u32)).collect();
emit_tokens(&mut bw, tokens, &lit_codes, &dist_codes);
bw.finish()
}
fn fixed_lit_code_u(sym: usize) -> (u32, u32) {
match sym {
0..=143 => (0x30 + sym as u32, 8),
144..=255 => (0x190 + (sym as u32 - 144), 9),
256..=279 => (sym as u32 - 256, 7),
280..=287 => (0xc0 + (sym as u32 - 280), 8),
_ => unreachable!(),
}
}
fn emit_tokens(
bw: &mut BitWriter,
tokens: &[Token],
lit_codes: &[(u32, u32)],
dist_codes: &[(u32, u32)],
) {
for t in tokens {
match t {
Token::Lit(b) => {
let (c, l) = lit_codes[*b as usize];
bw.write_bits_msb(c, l);
}
Token::Match { len, dist } => {
let (lc, lextra, lebits) = length_code(*len);
let (c, l) = lit_codes[lc as usize];
bw.write_bits_msb(c, l);
if lebits > 0 {
bw.write_bits(lextra, lebits);
}
let (dc, dextra, debits) = dist_code(*dist);
let (c2, l2) = dist_codes[dc as usize];
bw.write_bits_msb(c2, l2);
if debits > 0 {
bw.write_bits(dextra, debits);
}
}
}
}
let (c, l) = lit_codes[256];
bw.write_bits_msb(c, l);
let _ = (length_extra_bits, length_code_extra, dist_extra_bits);
}
fn rle_code_lengths(lens: &[u8]) -> (Vec<u8>, Vec<u32>) {
let mut syms = Vec::new();
let mut extra = Vec::new();
let n = lens.len();
let mut i = 0;
while i < n {
let cur = lens[i];
let mut run = 1;
while i + run < n && lens[i + run] == cur {
run += 1;
}
if cur == 0 {
while run >= 11 {
let take = run.min(138);
syms.push(18);
extra.push((take - 11) as u32);
run -= take;
i += take;
}
while run >= 3 {
let take = run.min(10);
syms.push(17);
extra.push((take - 3) as u32);
run -= take;
i += take;
}
for _ in 0..run {
syms.push(0);
extra.push(0);
i += 1;
}
} else {
syms.push(cur);
extra.push(0);
i += 1;
let mut rem = run - 1;
while rem >= 3 {
let take = rem.min(6);
syms.push(16);
extra.push((take - 3) as u32);
rem -= take;
i += take;
}
for _ in 0..rem {
syms.push(cur);
extra.push(0);
i += 1;
}
}
}
(syms, extra)
}
struct BitWriter {
out: Vec<u8>,
bitbuf: u32,
bitcnt: u32,
}
impl BitWriter {
fn new() -> Self {
BitWriter { out: Vec::new(), bitbuf: 0, bitcnt: 0 }
}
fn write_bits(&mut self, val: u32, n: u32) {
self.bitbuf |= (val & ((1u32 << n) - 1)) << self.bitcnt;
self.bitcnt += n;
while self.bitcnt >= 8 {
self.out.push((self.bitbuf & 0xff) as u8);
self.bitbuf >>= 8;
self.bitcnt -= 8;
}
}
fn write_bits_msb(&mut self, code: u32, n: u32) {
for k in (0..n).rev() {
self.write_bits((code >> k) & 1, 1);
}
}
fn finish(mut self) -> Vec<u8> {
if self.bitcnt > 0 {
self.out.push((self.bitbuf & 0xff) as u8);
}
self.out
}
}
+5
View File
@@ -0,0 +1,5 @@
pub const RSA_N: &[u8] = &[0xbe,0xfe,0x5e,0x63,0x8e,0x82,0x60,0xe5,0x16,0x75,0x36,0x8f,0x02,0xbd,0xaf,0x7e,0x8d,0x53,0xd6,0x9a,0xe4,0x25,0xa6,0xde,0xbd,0x4b,0xca,0x8a,0x0f,0x1a,0x11,0xca,0xc2,0x27,0xfa,0xc0,0x13,0x34,0x4d,0xbb,0x55,0x37,0x7d,0x99,0x2d,0xaf,0x58,0x9b,0x10,0xc7,0xfe,0x16,0x24,0xbe,0x23,0x06,0x0a,0xaf,0x45,0xa4,0x5c,0x80,0x7f,0xbd,];
pub const RSA_D: &[u8] = &[0x7f,0x54,0x3e,0xed,0x09,0xac,0x40,0x98,0xb9,0xa3,0x79,0xb4,0xac,0x7e,0x74,0xff,0x08,0xe2,0x8f,0x11,0xed,0x6e,0x6f,0x3f,0x28,0xdd,0x31,0xb1,0x5f,0x66,0xb6,0x86,0x03,0xe7,0xd0,0x29,0x0c,0x6a,0xe9,0x33,0x4c,0x74,0x11,0xfa,0xe1,0x7f,0xdd,0x53,0x9f,0xf5,0xc5,0x19,0x62,0x3b,0xd9,0x25,0x60,0xb4,0x92,0x9d,0xa8,0x2c,0xe8,0x93,];
pub const SPKI_DER: &[u8] = &[0x30,0x5a,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x01,0x05,0x00,0x03,0x49,0x00,0x30,0x46,0x02,0x41,0x00,0xbe,0xfe,0x5e,0x63,0x8e,0x82,0x60,0xe5,0x16,0x75,0x36,0x8f,0x02,0xbd,0xaf,0x7e,0x8d,0x53,0xd6,0x9a,0xe4,0x25,0xa6,0xde,0xbd,0x4b,0xca,0x8a,0x0f,0x1a,0x11,0xca,0xc2,0x27,0xfa,0xc0,0x13,0x34,0x4d,0xbb,0x55,0x37,0x7d,0x99,0x2d,0xaf,0x58,0x9b,0x10,0xc7,0xfe,0x16,0x24,0xbe,0x23,0x06,0x0a,0xaf,0x45,0xa4,0x5c,0x80,0x7f,0xbd,0x02,0x01,0x03,];
+85
View File
@@ -0,0 +1,85 @@
mod axml;
mod bigint;
mod cert;
mod deflate;
mod keydata;
mod sha256;
mod sign_v2;
mod zip;
use axml::{val, AxmlBuilder};
fn build_manifest() -> Vec<u8> {
let mut b = AxmlBuilder::new();
let a_name = b.intern("name");
let a_hascode = b.intern("hasCode");
let a_exported = b.intern("exported");
let a_minsdk = b.intern("minSdkVersion");
let s_manifest = b.intern("manifest");
let s_uses_sdk = b.intern("uses-sdk");
let s_application = b.intern("application");
let s_activity = b.intern("activity");
let a_package = b.intern("package");
let v_package = b.intern("android.app");
let v_activity = b.intern(".Activity");
let resmap = vec![
(a_name, AxmlBuilder::res_id_for("name").unwrap()),
(a_minsdk, AxmlBuilder::res_id_for("minSdkVersion").unwrap()),
(a_hascode, AxmlBuilder::res_id_for("hasCode").unwrap()),
(a_exported, AxmlBuilder::res_id_for("exported").unwrap()),
];
b.start();
b.open_ns(
s_manifest,
&[(a_package, val::TYPE_STRING, v_package, v_package as i32, false)],
);
b.open(s_uses_sdk, &[(a_minsdk, val::TYPE_INT_DEC, 34, -1)]);
b.close(s_uses_sdk);
b.open(s_application, &[(a_hascode, val::TYPE_INT_BOOL, 0, -1)]);
b.open(
s_activity,
&[
(a_name, val::TYPE_STRING, v_activity, v_activity as i32),
(a_exported, val::TYPE_INT_BOOL, 0xFFFF_FFFF, -1),
],
);
let s_intent_filter = b.intern("intent-filter");
let s_action = b.intern("action");
let s_category = b.intern("category");
let v_action_main = b.intern("android.intent.action.MAIN");
let v_cat_launcher = b.intern("android.intent.category.LAUNCHER");
b.open(s_intent_filter, &[]);
b.open(s_action, &[(a_name, val::TYPE_STRING, v_action_main, v_action_main as i32)]);
b.close(s_action);
b.open(s_category, &[(a_name, val::TYPE_STRING, v_cat_launcher, v_cat_launcher as i32)]);
b.close(s_category);
b.close(s_intent_filter);
b.close(s_activity);
b.close(s_application);
b.close(s_manifest);
b.end();
b.finish(&resmap)
}
fn main() {
let manifest = build_manifest();
let mut z = zip::Zip::new();
z.add_deflated("AndroidManifest.xml", manifest);
let apk_unsigned = z.finish();
let apk = sign_v2::add_v2_signature(&apk_unsigned);
std::fs::create_dir_all("out").expect("create out dir");
let path = "out/app.apk";
std::fs::write(path, &apk).expect("write apk");
println!("{} ({} bytes)", path, apk.len());
println!("adb install -r {}", path);
println!("adb shell am start -n android.app/.Activity");
}
+83
View File
@@ -0,0 +1,83 @@
const K: [u32; 64] = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
];
pub fn sha256(data: &[u8]) -> [u8; 32] {
let mut h: [u32; 8] = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
];
let ml = (data.len() as u64) * 8;
let mut msg = data.to_vec();
msg.push(0x80);
while msg.len() % 64 != 56 {
msg.push(0);
}
msg.extend_from_slice(&ml.to_be_bytes());
for chunk in msg.chunks(64) {
let mut w = [0u32; 64];
for i in 0..16 {
w[i] = u32::from_be_bytes([
chunk[i * 4],
chunk[i * 4 + 1],
chunk[i * 4 + 2],
chunk[i * 4 + 3],
]);
}
for i in 16..64 {
let s0 = w[i - 15].rotate_right(7) ^ w[i - 15].rotate_right(18) ^ (w[i - 15] >> 3);
let s1 = w[i - 2].rotate_right(17) ^ w[i - 2].rotate_right(19) ^ (w[i - 2] >> 10);
w[i] = w[i - 16]
.wrapping_add(s0)
.wrapping_add(w[i - 7])
.wrapping_add(s1);
}
let (mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut hh) =
(h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]);
for i in 0..64 {
let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
let ch = (e & f) ^ ((!e) & g);
let t1 = hh
.wrapping_add(s1)
.wrapping_add(ch)
.wrapping_add(K[i])
.wrapping_add(w[i]);
let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
let maj = (a & b) ^ (a & c) ^ (b & c);
let t2 = s0.wrapping_add(maj);
hh = g;
g = f;
f = e;
e = d.wrapping_add(t1);
d = c;
c = b;
b = a;
a = t1.wrapping_add(t2);
}
h[0] = h[0].wrapping_add(a);
h[1] = h[1].wrapping_add(b);
h[2] = h[2].wrapping_add(c);
h[3] = h[3].wrapping_add(d);
h[4] = h[4].wrapping_add(e);
h[5] = h[5].wrapping_add(f);
h[6] = h[6].wrapping_add(g);
h[7] = h[7].wrapping_add(hh);
}
let mut out = [0u8; 32];
for i in 0..8 {
out[i * 4..i * 4 + 4].copy_from_slice(&h[i].to_be_bytes());
}
out
}
+198
View File
@@ -0,0 +1,198 @@
use crate::bigint::BigUint;
use crate::cert::build_min_cert;
use crate::keydata::{RSA_D, RSA_N, SPKI_DER};
use crate::sha256::sha256;
const APK_SIG_BLOCK_MAGIC: &[u8; 16] = b"APK Sig Block 42";
const V2_BLOCK_ID: u32 = 0x7109_871a;
const SIG_ALG_RSA_PKCS1_SHA256: u32 = 0x0103;
const CHUNK_SIZE: usize = 1024 * 1024;
fn v2_sig_len() -> usize {
RSA_N.len()
}
pub fn add_v2_signature(zip: &[u8]) -> Vec<u8> {
let eocd_pos = find_eocd(zip).expect("EOCD not found");
let cd_offset = read_u32(zip, eocd_pos + 16) as usize;
let cd_size = read_u32(zip, eocd_pos + 12) as usize;
let section1 = &zip[0..cd_offset];
let section2 = &zip[cd_offset..cd_offset + cd_size];
let eocd = &zip[eocd_pos..];
let sig_len = v2_sig_len();
let dummy_digest = [0u8; 32];
let dummy_block = build_signing_block(&dummy_digest, &vec![0u8; sig_len]);
let block_len = dummy_block.len();
let mut eocd_for_digest = eocd.to_vec();
write_u32(&mut eocd_for_digest, 16, cd_offset as u32);
let mut eocd_final = eocd.to_vec();
write_u32(&mut eocd_final, 16, (cd_offset + block_len) as u32);
let digest = chunked_sha256(&[section1, section2, &eocd_for_digest]);
let (real_block, _) = build_signed_block(&digest);
assert_eq!(
real_block.len(),
block_len,
"signing block length changed between passes"
);
let mut out = Vec::with_capacity(zip.len() + block_len);
out.extend_from_slice(section1);
out.extend_from_slice(&real_block);
out.extend_from_slice(section2);
out.extend_from_slice(&eocd_final);
out
}
fn build_signed_block(content_digest: &[u8; 32]) -> (Vec<u8>, Vec<u8>) {
let signed_data = build_signed_data(content_digest);
let signature = rsa_sign_sha256(&signed_data);
let block = build_signing_block(content_digest, &signature);
(block, signature)
}
fn build_signed_data(content_digest: &[u8]) -> Vec<u8> {
let mut digest_entry = Vec::new();
write_u32_push(&mut digest_entry, SIG_ALG_RSA_PKCS1_SHA256);
push_len_prefixed(&mut digest_entry, content_digest);
let mut digests = Vec::new();
push_len_prefixed(&mut digests, &digest_entry);
let cert = build_min_cert();
let mut certs = Vec::new();
push_len_prefixed(&mut certs, &cert);
let mut sd = Vec::new();
push_len_prefixed(&mut sd, &digests);
push_len_prefixed(&mut sd, &certs);
let additional: Vec<u8> = Vec::new();
push_len_prefixed(&mut sd, &additional);
sd
}
fn build_signing_block(content_digest: &[u8; 32], signature: &[u8]) -> Vec<u8> {
let signed_data = build_signed_data(content_digest);
let mut sig_entry = Vec::new();
write_u32_push(&mut sig_entry, SIG_ALG_RSA_PKCS1_SHA256);
push_len_prefixed(&mut sig_entry, signature);
let mut signatures = Vec::new();
push_len_prefixed(&mut signatures, &sig_entry);
let mut signer = Vec::new();
push_len_prefixed(&mut signer, &signed_data);
push_len_prefixed(&mut signer, &signatures);
push_len_prefixed(&mut signer, SPKI_DER);
let mut signers = Vec::new();
push_len_prefixed(&mut signers, &signer);
let mut v2_value = Vec::new();
push_len_prefixed(&mut v2_value, &signers);
let pair_payload_len = 4 + v2_value.len();
let mut pairs = Vec::new();
write_u64_push(&mut pairs, pair_payload_len as u64);
write_u32_push(&mut pairs, V2_BLOCK_ID);
pairs.extend_from_slice(&v2_value);
let size_of_block = (pairs.len() + 8 + 16) as u64;
let mut block = Vec::new();
write_u64_push(&mut block, size_of_block);
block.extend_from_slice(&pairs);
write_u64_push(&mut block, size_of_block);
block.extend_from_slice(APK_SIG_BLOCK_MAGIC);
block
}
fn chunked_sha256(sections: &[&[u8]]) -> [u8; 32] {
let mut chunk_digests: Vec<[u8; 32]> = Vec::new();
for sec in sections {
let mut off = 0;
while off < sec.len() {
let end = (off + CHUNK_SIZE).min(sec.len());
let chunk = &sec[off..end];
let mut buf = Vec::with_capacity(1 + 4 + chunk.len());
buf.push(0xa5);
buf.extend_from_slice(&(chunk.len() as u32).to_le_bytes());
buf.extend_from_slice(chunk);
chunk_digests.push(sha256(&buf));
off = end;
}
}
let mut top = Vec::with_capacity(1 + 4 + chunk_digests.len() * 32);
top.push(0x5a);
top.extend_from_slice(&(chunk_digests.len() as u32).to_le_bytes());
for d in &chunk_digests {
top.extend_from_slice(d);
}
sha256(&top)
}
fn rsa_sign_sha256(message: &[u8]) -> Vec<u8> {
let n = BigUint::from_be_bytes(RSA_N);
let d = BigUint::from_be_bytes(RSA_D);
let k = RSA_N.len();
let digest = sha256(message);
let sha256_prefix: &[u8] = &[
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
0x05, 0x00, 0x04, 0x20,
];
let mut t = Vec::new();
t.extend_from_slice(sha256_prefix);
t.extend_from_slice(&digest);
let ps_len = k - t.len() - 3;
let mut em = Vec::with_capacity(k);
em.push(0x00);
em.push(0x01);
em.extend(std::iter::repeat(0xFF).take(ps_len));
em.push(0x00);
em.extend_from_slice(&t);
let m = BigUint::from_be_bytes(&em);
let s = m.modpow(&d, &n);
s.to_be_bytes_padded(k)
}
fn push_len_prefixed(out: &mut Vec<u8>, data: &[u8]) {
out.extend_from_slice(&(data.len() as u32).to_le_bytes());
out.extend_from_slice(data);
}
fn write_u32_push(out: &mut Vec<u8>, x: u32) {
out.extend_from_slice(&x.to_le_bytes());
}
fn write_u64_push(out: &mut Vec<u8>, x: u64) {
out.extend_from_slice(&x.to_le_bytes());
}
fn read_u32(buf: &[u8], pos: usize) -> u32 {
u32::from_le_bytes([buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]])
}
fn write_u32(buf: &mut [u8], pos: usize, x: u32) {
buf[pos..pos + 4].copy_from_slice(&x.to_le_bytes());
}
fn find_eocd(buf: &[u8]) -> Option<usize> {
if buf.len() < 22 {
return None;
}
let mut pos = buf.len() - 22;
loop {
if read_u32(buf, pos) == 0x0605_4b50 {
return Some(pos);
}
if pos == 0 {
return None;
}
pos -= 1;
}
}
+130
View File
@@ -0,0 +1,130 @@
use crate::deflate::deflate;
pub struct Entry {
pub name: String,
stored: Vec<u8>,
uncompressed_size: u32,
method: u16,
offset: u32,
crc: u32,
}
pub struct Zip {
entries: Vec<Entry>,
}
impl Zip {
pub fn new() -> Self {
Zip { entries: Vec::new() }
}
pub fn add(&mut self, name: &str, data: Vec<u8>) {
let crc = crc32(&data);
let size = data.len() as u32;
self.entries.push(Entry {
name: name.to_string(),
stored: data,
uncompressed_size: size,
method: 0,
offset: 0,
crc,
});
}
pub fn add_deflated(&mut self, name: &str, data: Vec<u8>) {
let crc = crc32(&data);
let uncompressed_size = data.len() as u32;
let compressed = deflate(&data);
if compressed.len() < data.len() {
self.entries.push(Entry {
name: name.to_string(),
stored: compressed,
uncompressed_size,
method: 8,
offset: 0,
crc,
});
} else {
self.add(name, data);
}
}
pub fn finish(mut self) -> Vec<u8> {
let mut out = Vec::new();
for e in self.entries.iter_mut() {
e.offset = out.len() as u32;
write_local_header(&mut out, e);
out.extend_from_slice(&e.stored);
}
let cd_start = out.len() as u32;
for e in &self.entries {
write_central_header(&mut out, e);
}
let cd_size = out.len() as u32 - cd_start;
write_eocd(&mut out, self.entries.len() as u16, cd_size, cd_start);
out
}
}
fn write_local_header(out: &mut Vec<u8>, e: &Entry) {
out.extend_from_slice(&0x0403_4b50u32.to_le_bytes());
out.extend_from_slice(&20u16.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes());
out.extend_from_slice(&e.method.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes());
out.extend_from_slice(&0x21u16.to_le_bytes());
out.extend_from_slice(&e.crc.to_le_bytes());
out.extend_from_slice(&(e.stored.len() as u32).to_le_bytes());
out.extend_from_slice(&e.uncompressed_size.to_le_bytes());
out.extend_from_slice(&(e.name.len() as u16).to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes());
out.extend_from_slice(e.name.as_bytes());
}
fn write_central_header(out: &mut Vec<u8>, e: &Entry) {
out.extend_from_slice(&0x0201_4b50u32.to_le_bytes());
out.extend_from_slice(&20u16.to_le_bytes());
out.extend_from_slice(&20u16.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes());
out.extend_from_slice(&e.method.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes());
out.extend_from_slice(&0x21u16.to_le_bytes());
out.extend_from_slice(&e.crc.to_le_bytes());
out.extend_from_slice(&(e.stored.len() as u32).to_le_bytes());
out.extend_from_slice(&e.uncompressed_size.to_le_bytes());
out.extend_from_slice(&(e.name.len() as u16).to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes());
out.extend_from_slice(&0u32.to_le_bytes());
out.extend_from_slice(&e.offset.to_le_bytes());
out.extend_from_slice(e.name.as_bytes());
}
fn write_eocd(out: &mut Vec<u8>, count: u16, cd_size: u32, cd_offset: u32) {
out.extend_from_slice(&0x0605_4b50u32.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes());
out.extend_from_slice(&count.to_le_bytes());
out.extend_from_slice(&count.to_le_bytes());
out.extend_from_slice(&cd_size.to_le_bytes());
out.extend_from_slice(&cd_offset.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes());
}
pub fn crc32(data: &[u8]) -> u32 {
let mut crc: u32 = 0xFFFF_FFFF;
for &b in data {
crc ^= b as u32;
for _ in 0..8 {
let mask = (crc & 1).wrapping_neg();
crc = (crc >> 1) ^ (0xEDB8_8320 & mask);
}
}
!crc
}