Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor wide string constants #36

Merged
merged 1 commit into from
Feb 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 4 additions & 94 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ Compiletime string constant obfuscation.

use core::{char, fmt, ops, ptr, str};

#[doc(hidden)]
pub mod wide;

//----------------------------------------------------------------

/// Compiletime random number generator.
Expand Down Expand Up @@ -160,99 +163,6 @@ pub const fn position(haystack: &str, needle: &str) -> ops::Range<usize> {

//----------------------------------------------------------------

/// Wide string constant, returns an array of words.
///
/// The type of the returned constant is `&'static [u16; LEN]`.
///
/// ```
/// let expected = &['W' as u16, 'i' as u16, 'd' as u16, 'e' as u16, 0];
/// assert_eq!(obfstr::wide!("Wide\0"), expected);
/// ```
#[macro_export]
macro_rules! wide {
($s:expr) => {{
const STRING: &str = $s;
const LEN: usize = $crate::wide_len(STRING);
const WIDE: [u16; LEN] = $crate::wide::<LEN>(STRING);
&WIDE
}};
}

#[doc(hidden)]
pub const fn wide_len(s: &str) -> usize {
let s = s.as_bytes();
let mut len = 0usize;
let mut i = 0usize;
while i < s.len() {
let chr;
if s[i] & 0x80 == 0x00 {
chr = s[i] as u32;
i += 1;
}
else if s[i] & 0xe0 == 0xc0 {
chr = (s[i] as u32 & 0x1f) << 6 | (s[i + 1] as u32 & 0x3f);
i += 2;
}
else if s[i] & 0xf0 == 0xe0 {
chr = (s[i] as u32 & 0x0f) << 12 | (s[i + 1] as u32 & 0x3f) << 6 | (s[i + 2] as u32 & 0x3f);
i += 3;
}
else if s[i] & 0xf8 == 0xf0 {
chr = (s[i] as u32 & 0x07) << 18 | (s[i + 1] as u32 & 0x3f) << 12 | (s[i + 2] as u32 & 0x3f) << 6 | (s[i + 3] as u32 & 0x3f);
i += 4;
}
else {
// unimplemented!()
loop { }
};
len += if chr >= 0x10000 { 2 } else { 1 };
}
return len;
}

#[doc(hidden)]
pub const fn wide<const LEN: usize>(s: &str) -> [u16; LEN] {
let s = s.as_bytes();
let mut data = [0u16; LEN];
let mut i = 0usize;
let mut j = 0usize;
while i < s.len() {
let chr;
if s[i] & 0x80 == 0x00 {
chr = s[i] as u32;
i += 1;
}
else if s[i] & 0xe0 == 0xc0 {
chr = (s[i] as u32 & 0x1f) << 6 | (s[i + 1] as u32 & 0x3f);
i += 2;
}
else if s[i] & 0xf0 == 0xe0 {
chr = (s[i] as u32 & 0x0f) << 12 | (s[i + 1] as u32 & 0x3f) << 6 | (s[i + 2] as u32 & 0x3f);
i += 3;
}
else if s[i] & 0xf8 == 0xf0 {
chr = (s[i] as u32 & 0x07) << 18 | (s[i + 1] as u32 & 0x3f) << 12 | (s[i + 2] as u32 & 0x3f) << 6 | (s[i + 3] as u32 & 0x3f);
i += 4;
}
else {
// unimplemented!()
loop { }
};
if chr >= 0x10000 {
data[j + 0] = (0xD800 + (chr - 0x10000) / 0x400) as u16;
data[j + 1] = (0xDC00 + (chr - 0x10000) % 0x400) as u16;
j += 2;
}
else {
data[j] = chr as u16;
j += 1;
}
}
return data;
}

//----------------------------------------------------------------

/// Obfuscated string constant data.
///
/// This type represents the data baked in the binary and holds the key and obfuscated string.
Expand Down Expand Up @@ -354,7 +264,7 @@ impl<const LEN: usize> ObfString<[u16; LEN]> {
#[doc(hidden)]
pub const fn obfuscate(key: u32, string: &str) -> ObfString<[u16; LEN]> {
let keys = self::words::keystream::<LEN>(key);
let string = wide::<LEN>(string);
let string = self::wide::encode::<LEN>(string);
let data = self::words::obfuscate::<LEN>(&string, &keys);
ObfString { key, data }
}
Expand Down
92 changes: 92 additions & 0 deletions src/wide.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@

/// Encodes the input string as a wide string (utf-16) constant.
///
/// The type of the returned constant is `&'static [u16; LEN]`.
///
/// # Examples
///
/// ```
/// let expected = &['W' as u16, 'i' as u16, 'd' as u16, 'e' as u16, 0];
/// assert_eq!(expected, obfstr::wide!("Wide\0"));
/// ```
#[macro_export]
macro_rules! wide {
($s:expr) => {{
const _OBFSTR_RPWZ_STRING: &str = $s;
const _OBFSTR_NKVU_LEN: usize = $crate::wide::len(_OBFSTR_RPWZ_STRING);
const _OBFSTR_CVUA_WIDE: [u16; _OBFSTR_NKVU_LEN] = $crate::wide::encode::<_OBFSTR_NKVU_LEN>(_OBFSTR_RPWZ_STRING);
&_OBFSTR_CVUA_WIDE
}};
}

const fn next(bytes: &[u8]) -> Option<(u32, &[u8])> {
match bytes {
&[a, ref tail @ ..] if a & 0x80 == 0x00 =>
Some((a as u32, tail)),
&[a, b, ref tail @ ..] if a & 0xe0 == 0xc0 =>
Some(((a as u32 & 0x1f) << 6 | (b as u32 & 0x3f), tail)),
&[a, b, c, ref tail @ ..] if a & 0xf0 == 0xe0 =>
Some(((a as u32 & 0x0f) << 12 | (b as u32 & 0x3f) << 6 | (c as u32 & 0x3f), tail)),
&[a, b, c, d, ref tail @ ..] if a & 0xf8 == 0xf0 =>
Some(((a as u32 & 0x07) << 18 | (b as u32 & 0x3f) << 12 | (c as u32 & 0x3f) << 6 | (d as u32 & 0x3f), tail)),
&[..] => None,
}
}

#[doc(hidden)]
pub const fn len(s: &str) -> usize {
let mut bytes = s.as_bytes();
let mut len = 0;
while let Some((chr, tail)) = next(bytes) {
bytes = tail;
len += if chr >= 0x10000 { 2 } else { 1 };
}
return len;
}

#[doc(hidden)]
pub const fn encode<const LEN: usize>(s: &str) -> [u16; LEN] {
let mut bytes = s.as_bytes();
let mut data = [0u16; LEN];
let mut i = 0usize;
while let Some((chr, tail)) = next(bytes) {
bytes = tail;
if chr >= 0x10000 {
data[i + 0] = (0xD800 + (chr - 0x10000) / 0x400) as u16;
data[i + 1] = (0xDC00 + (chr - 0x10000) % 0x400) as u16;
i += 2;
}
else {
data[i] = chr as u16;
i += 1;
}
}
return data;
}

#[test]
fn test_example() {
let text = &['e' as u16, 'x' as u16, 'a' as u16, 'm' as u16, 'p' as u16, 'l' as u16, 'e' as u16];
assert_eq!(text, wide!("example"));
}

#[test]
fn test_escapes() {
let text = &['\t' as u16, '\n' as u16, '\r' as u16, '\\' as u16, '\0' as u16, '\'' as u16, '\"' as u16, '\x52' as u16, '\u{00B6}' as u16];
assert_eq!(text, wide!("\t\n\r\\\0\'\"\x52\u{00B6}"));
}

#[test]
fn test_raw() {
let text = &[b'\\' as u16];
assert_eq!(text, wide!(r"\"));
assert_eq!(text, wide!(r#"\"#));
assert_eq!(text, wide!(r##"\"##));
}

#[test]
fn test_const() {
const STRING: &str = "Wide\0";
let text = &['W' as u16, 'i' as u16, 'd' as u16, 'e' as u16, 0];
assert_eq!(text, wide!(STRING));
}