-
Notifications
You must be signed in to change notification settings - Fork 28
/
steganographr-api.php
175 lines (148 loc) · 7.92 KB
/
steganographr-api.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
<?php
header("Content-type: text/html; charset=utf-8");
// Convert a string into binary data
function str2bin($text){
$bin = array();
for($i=0; strlen($text)>$i; $i++)
$bin[] = decbin(ord($text[$i]));
return implode(' ', $bin);
}
// Wrap a string with a distinct boundary
function wrap($string) {
return "\xEF\xBB\xBF".$string."\xEF\xBB\xBF"; // Unicode Character 'ZERO WIDTH NON-BREAKING SPACE' (U+FEFF) 0xEF 0xBB 0xBF
}
// Unwrap a string if the distinct boundary exists
function unwrap($string) {
$tmp = explode("\xEF\xBB\xBF", $string);
if(count($tmp) == 1) return false; // If the string doesn't contain the boundary, return false
return $tmp[1]; // Otherwise, return the unwrapped string
}
// Convert binary data into a string
function bin2str($bin){
$text = array();
$bin = explode(' ', $bin);
for($i=0; count($bin)>$i; $i++)
$text[] = chr(@bindec($bin[$i]));
return implode($text);
}
// Convert the ones, zeros, and spaces of the hidden binary data to their respective zero-width characters
function bin2hidden($str) {
$str = str_replace(' ', "\xE2\x81\xA0", $str); // Unicode Character 'WORD JOINER' (U+2060) 0xE2 0x81 0xA0
$str = str_replace('0', "\xE2\x80\x8B", $str); // Unicode Character 'ZERO WIDTH SPACE' (U+200B) 0xE2 0x80 0x8B
$str = str_replace('1', "\xE2\x80\x8C", $str); // Unicode Character 'ZERO WIDTH NON-JOINER' (U+200C) 0xE2 0x80 0x8C
return $str;
}
// Convert zero-width characters to hidden binary data
function hidden2bin($str) {
$str = str_replace("\xE2\x81\xA0", ' ', $str); // Unicode Character 'WORD JOINER' (U+2060) 0xE2 0x81 0xA0
$str = str_replace("\xE2\x80\x8B", '0', $str); // Unicode Character 'ZERO WIDTH SPACE' (U+200B) 0xE2 0x80 0x8B
$str = str_replace("\xE2\x80\x8C", '1', $str); // Unicode Character 'ZERO WIDTH NON-JOINER' (U+200C) 0xE2 0x80
return $str;
}
if(!isset($_GET['public']) && !isset($_GET['private']) && !isset($_GET['decode'])) {
echo '<!DOCTYPE html>
<meta charset="utf-8">
<title>Steganographr API</title>
<h1>Steganographr API</h1>
<p>Hello and thanks for your interest in this API. Here’s some quick documentation:</p>
<h2>Encoding</h2>
<p>You can encode messages by passing both <code>public</code> and <code>private</code> parameters, e.g.:</p>
<blockquote>
<code>
GET <a href="https://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].'?public=It+sure+is+hot+today&private=meet+me+behind+the+office+at+4+pm">https://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].'?public=It+sure+is+hot+today&private=meet+me+behind+the+office+at+4+pm</a>
</code>
</blockquote>
<p>The response will be the encoded text, e.g.:</p>
<blockquote>It sure is hot today</blockquote>
<h2>Decoding</h2>
<p>You can decode messages by passing the <code>decode</code> parameter, e.g.:</p>
<blockquote>
<code>
GET <a href="https://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].'?decode=It+sure+is+hot+today">https://neatnik.net/steganographr/api?decode=It+sure+is+hot+today</a>
</code>
</blockquote>
<p>The response will be the private message, e.g.:</p>
<blockquote>
<code>
meet me behind the office at 4 pm
</code>
</blockquote>
<h2>Additional information</h2>
<p>You can use an interactive version of the service at <a href="https://neatnik.net/steganographr/">https://neatnik.net/steganographr/</a>. If you have any questions, feel free to contact <a href="mailto:[email protected]">[email protected]</a>.</p>
<hr>
<em>Last updated: '.date("r", filemtime(__FILE__)).'</em>
';
exit;
}
if(!isset($_GET['decode']) && isset($_GET['public']) && !isset($_GET['private'])) {
die("<strong>Failed:</strong> You need to specify your private message in the <code>private</code> parameter.");
}
if(!isset($_GET['decode']) && isset($_GET['private']) && !isset($_GET['public'])) {
die("<strong>Failed:</strong> You need to specify your public message in the <code>public</code> parameter.");
}
if(isset($_GET['public']) && strlen($_GET['public']) >= 2) {
// Grab the public message string and break it up into characters
$public = $_REQUEST['public'];
$public = mb_str_split($public);
// Find the half-way point in the string
$half = round(count($public) / 2);
// Grab the private message
$private = $_REQUEST['private'];
// Convert it to binary data
$private = str2bin($private);
// And convert that into a string of zero-width characters
$private = bin2hidden($private);
// Finally, wrap it with a distinct boundary character
$private = wrap($private);
// Inject the encoded private message into the approximate half-way point in the public string
$i = 0;
$tmp = array();
if(count($public) == 1) {
$tmp[0] = $public[0];
$tmp[1] = $private;
}
else {
foreach($public as $char) {
if($i == $half) {
$tmp[] = $private;
}
$tmp[] = $char;
$i++;
}
}
// Reassemble the public string
$public = implode('', $tmp);
die($public);
$out['steganographized'] = $public;
}
else if(isset($_GET['public'])) {
die('<strong>Failed:</strong> Your public message must be at least two characters.');
}
if(isset($_GET['decode'])) {
// Unhide the message
$unwrapped = unwrap($_REQUEST['decode']);
// If it's not wrapped, process the full string as received
if(!$unwrapped) {
$message = bin2str(hidden2bin($_REQUEST['decode']));
}
// Otherwise, process only the unwrapped string
else {
$message = bin2str(hidden2bin($unwrapped));
}
// If encoded ampersands are present, convert them to regular ampersands
$message = str_replace('&', '&', $message);
// Display the hidden private message
if(strlen($message) < 2) {
die('<strong>Notice:</strong> No private message was found.');
}
else {
$message = htmlentities($message);
$bits = str_split($message);
$out = null;
foreach($bits as $char) {
if(ord($char) == 0) continue;
$out .= $char;
}
die($out);
}
}