kindle_hacks 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ v0.1. Initial release.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2009 Zergling.Net
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,17 @@
1
+ CHANGELOG
2
+ LICENSE
3
+ Manifest
4
+ README.textile
5
+ Rakefile
6
+ bin/kindle_update
7
+ lib/kindle_hacks.rb
8
+ lib/kindle_hacks/update.rb
9
+ lib/kindle_hacks/update_hl.rb
10
+ test/fixtures/update.dat
11
+ test/fixtures/update/001-savory.sh
12
+ test/fixtures/update/999-reboot.sh
13
+ test/fixtures/update/savoryctl
14
+ test/fixtures/update/update.yml
15
+ test/fixtures/update_dx_web.bin
16
+ test/fixtures/update_k2_pdf.bin
17
+ test/update_test.rb
@@ -0,0 +1,18 @@
1
+ h1. KindleHacks
2
+
3
+ This gem contains code that's useful for hacking on the kindle.
4
+
5
+ h2. Features
6
+
7
+ Currently, the gem installs the command-line tool kindle_update that can be used
8
+ to build and unpack Kindle updates. KindleHacks::Update implements the API used
9
+ by the command-line tool.
10
+
11
+ h2. Acknowledgments
12
+
13
+ The update packing and unpacking is based on igorsk's article available at
14
+ http://igorsk.blogspot.com/2007/12/hacking-kindle-part-2-bootloader-and.html
15
+
16
+ h2. Contributions
17
+
18
+ Please don't hesitate to fork the project and send pull requests.
@@ -0,0 +1,31 @@
1
+ # Rakefile that uses echoe to manage kindle_hacks' gemspec.
2
+ #
3
+ # Author:: Victor Costan
4
+ # Copyright:: Copyright (C) 2009 Zergling.Net
5
+ # License:: MIT
6
+
7
+ require 'rubygems'
8
+ require 'echoe'
9
+
10
+ Echoe.new('kindle_hacks') do |p|
11
+ p.project = 'zerglings' # rubyforge project
12
+
13
+ p.author = 'Victor Costan'
14
+ p.email = 'victor@zergling.net'
15
+ p.summary = "Assorted toolbox for hacking into Kindles."
16
+ p.url = 'http://github.com/costan/kindle_hacks'
17
+ p.dependencies = ['archive-tar-minitar >=0.5.2']
18
+ p.development_dependencies = ["echoe >=3.1.1", "flexmock >=0.8.6"]
19
+ p.eval = proc do |p|
20
+ p.default_executable = 'bin/kindle_update'
21
+ end
22
+
23
+ p.need_tar_gz = true
24
+ p.need_zip = true
25
+ p.rdoc_pattern = /^(lib|bin|tasks|ext)|^BUILD|^README|^CHANGELOG|^TODO|^LICENSE|^COPYING$/
26
+ end
27
+
28
+ if $0 == __FILE__
29
+ Rake.application = Rake::Application.new
30
+ Rake.application.run
31
+ end
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Command-line tool for packing and unpacking updates.
4
+ #
5
+ # Author:: Victor Costan
6
+ # Copyright:: Copyright (C) 2009 Zergling.Net
7
+ # License:: MIT
8
+
9
+ require 'fileutils'
10
+ require 'open-uri'
11
+
12
+ require 'rubygems'
13
+ require 'kindle_hacks'
14
+
15
+ case ARGV[0]
16
+ when 'pack'
17
+ update = KindleHacks::Update.read_dir ARGV[1] || '.'
18
+ File.open(File.join(ARGV[2] || '.', update.binary_file_name), 'w') do |f|
19
+ f.write update.to_binary
20
+ end
21
+ when 'unpack'
22
+ update = KindleHacks::Update.read File.read(ARGV[1])
23
+ FileUtils.mkdir_p ARGV[2] if ARGV[2]
24
+ update.to_dir ARGV[2] || '.'
25
+ else
26
+ print <<END_USAGE
27
+ Pack an update: #{$0} unpack [update_directory] [target_directory]
28
+ Unpack an update: #{$0} pack update_file [target_directory]
29
+ END_USAGE
30
+ end
@@ -0,0 +1,42 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{kindle_hacks}
5
+ s.version = "0.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Victor Costan"]
9
+ s.date = %q{2009-10-09}
10
+ s.default_executable = %q{bin/kindle_update}
11
+ s.description = %q{Assorted toolbox for hacking into Kindles.}
12
+ s.email = %q{victor@zergling.net}
13
+ s.executables = ["kindle_update"]
14
+ s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README.textile", "bin/kindle_update", "lib/kindle_hacks.rb", "lib/kindle_hacks/update.rb", "lib/kindle_hacks/update_hl.rb"]
15
+ s.files = ["CHANGELOG", "LICENSE", "Manifest", "README.textile", "Rakefile", "bin/kindle_update", "lib/kindle_hacks.rb", "lib/kindle_hacks/update.rb", "lib/kindle_hacks/update_hl.rb", "test/fixtures/update.dat", "test/fixtures/update/001-savory.sh", "test/fixtures/update/999-reboot.sh", "test/fixtures/update/savoryctl", "test/fixtures/update/update.yml", "test/fixtures/update_dx_web.bin", "test/fixtures/update_k2_pdf.bin", "test/update_test.rb", "kindle_hacks.gemspec"]
16
+ s.homepage = %q{http://github.com/costan/kindle_hacks}
17
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Kindle_hacks", "--main", "README.textile"]
18
+ s.require_paths = ["lib"]
19
+ s.rubyforge_project = %q{zerglings}
20
+ s.rubygems_version = %q{1.3.5}
21
+ s.summary = %q{Assorted toolbox for hacking into Kindles.}
22
+ s.test_files = ["test/update_test.rb"]
23
+
24
+ if s.respond_to? :specification_version then
25
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
26
+ s.specification_version = 3
27
+
28
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
29
+ s.add_runtime_dependency(%q<archive-tar-minitar>, [">= 0.5.2"])
30
+ s.add_development_dependency(%q<echoe>, [">= 3.1.1"])
31
+ s.add_development_dependency(%q<flexmock>, [">= 0.8.6"])
32
+ else
33
+ s.add_dependency(%q<archive-tar-minitar>, [">= 0.5.2"])
34
+ s.add_dependency(%q<echoe>, [">= 3.1.1"])
35
+ s.add_dependency(%q<flexmock>, [">= 0.8.6"])
36
+ end
37
+ else
38
+ s.add_dependency(%q<archive-tar-minitar>, [">= 0.5.2"])
39
+ s.add_dependency(%q<echoe>, [">= 3.1.1"])
40
+ s.add_dependency(%q<flexmock>, [">= 0.8.6"])
41
+ end
42
+ end
@@ -0,0 +1,8 @@
1
+ # Top-level include file for KindleHacks.
2
+ #
3
+ # Author:: Victor Costan
4
+ # Copyright:: Copyright (C) 2009 Zergling.Net
5
+ # License:: MIT
6
+
7
+ require 'kindle_hacks/update.rb'
8
+ require 'kindle_hacks/update_hl.rb'
@@ -0,0 +1,248 @@
1
+ # Code for dealing with the binary update file format.
2
+ #
3
+ # Author:: Victor Costan
4
+ # Copyright:: Copyright (C) 2009 Zergling.Net
5
+ # License:: MIT
6
+
7
+ require 'digest/md5'
8
+ require 'zlib'
9
+
10
+ require 'rubygems'
11
+ require 'archive/tar/minitar'
12
+
13
+ # :nodoc: namespace
14
+ module KindleHacks
15
+
16
+
17
+ # :nodoc: documented in update_hl.rb
18
+ class Update
19
+ # The device that the update targets, e.g. :kindle_dx.
20
+ attr_reader :device
21
+
22
+ # The files that constitute the update.
23
+ attr_reader :files
24
+
25
+ # The minimum firmware version that this update applies to.
26
+ #
27
+ # The recommended value is DEFAULT_MIN_VERSION.
28
+ attr_reader :min_version
29
+
30
+ # The maximum firmware version that this update applies to.
31
+ #
32
+ # The recommended value is DEFAULT_MAX_VERSION.
33
+ attr_reader :max_version
34
+
35
+ # The update's name, appended at the end of the update image and manifest.
36
+ #
37
+ # For example, an update named _Savory-0.06 will have its update file named
38
+ # update_Savory-0.06.bin and the manifest will be named
39
+ # update-Savory-0.06.dat.
40
+ attr_reader :name
41
+
42
+ # Whether this update is optional or not.
43
+ attr_reader :optional
44
+
45
+ # The update's type, e.g. :ota.
46
+ attr_reader :update
47
+
48
+ # Initializes an update from a potentially incomplete manifest.
49
+ def initialize(manifest = {})
50
+ @device = manifest[:device] || :kindle
51
+ @device = @device.to_sym
52
+ @files = manifest[:files] || []
53
+ @files.each do |file|
54
+ file[:display] ||= file[:name] + '_file'
55
+ file[:target] = file[:target].to_sym
56
+ end
57
+ @min_version = manifest[:min_version] || DEFAULT_MIN_VERSION
58
+ @max_version = manifest[:max_version] || DEFAULT_MAX_VERSION
59
+ @name = manifest[:name] || ''
60
+ @optional = manifest[:optional] || 0
61
+ @unknown = manifest[:unknown] || DEFAULT_UNKNOWN
62
+ @update = manifest[:signature] || :ota
63
+ @update = @update.to_sym
64
+ end
65
+
66
+ # Creates an Update from raw update bytes.
67
+ #
68
+ # Returns an Update object.
69
+ def self.read(raw_update)
70
+ header = decode_header raw_update
71
+
72
+ header_size = HEADER_SIZES[header[:update]]
73
+ descramble_key = (SCRAMBLE_KEY >> 4 | SCRAMBLE_KEY << 4) & 0xff
74
+ md5 = scramble! raw_update[16, 32], descramble_key
75
+ tgz = scramble! raw_update[header_size..-1], descramble_key
76
+ raise 'Update signature is invalid' unless Digest::MD5.hexdigest(tgz) == md5
77
+
78
+ Update.new header.merge(decode_files(tgz))
79
+ end
80
+
81
+ # Raw update bytes corresponding to an update.
82
+ #
83
+ # Returns a string that can be written to a .bin file for updating a device.
84
+ def to_binary
85
+ header = encoded_header
86
+ tgz = encoded_files
87
+ md5 = Digest::MD5.hexdigest tgz
88
+ scrambled_md5 = Update.scramble! md5, SCRAMBLE_KEY
89
+ scrambled_tgz = Update.scramble! tgz, SCRAMBLE_KEY
90
+
91
+ [header, scrambled_md5,
92
+ "\0" * (HEADER_SIZES[@update] - header.length - md5.length),
93
+ scrambled_tgz].join
94
+ end
95
+
96
+ # The update's binary file name.
97
+ def binary_file_name
98
+ "update#{@name}.bin"
99
+ end
100
+
101
+ # Decodes an update's header.
102
+ #
103
+ # Args:
104
+ # raw_header:: a string containing the raw update header
105
+ #
106
+ # Returns a hash that can be used as an argument to Update's constructor.
107
+ def self.decode_header(raw_header)
108
+ update_id, min_version, max_version, device_id, optional, unknown =
109
+ *raw_header[0, 16].unpack('a4VVvCC')
110
+
111
+ update = UPDATE_IDS.keys.find { |k| UPDATE_IDS[k] == update_id }
112
+ device = DEVICE_IDS.keys.find { |k| DEVICE_IDS[k] == device_id }
113
+
114
+ { :update => update, :min_version => min_version,
115
+ :max_version => max_version, :device => device, :optional => optional,
116
+ :unknown => unknown }
117
+ end
118
+
119
+ # The encoded header for this update.
120
+ def encoded_header
121
+ [UPDATE_IDS[@update], @min_version, @max_version, DEVICE_IDS[@device],
122
+ @optional, @unknown].pack('a4VVvCC')
123
+ end
124
+
125
+ # Decodes an update's file contents.
126
+ #
127
+ # Args:
128
+ # raw_tgz:: a string containing the raw update tar.gz bytes
129
+ #
130
+ # Returns a hash that can be used as the options argument to Update's
131
+ # constructor.
132
+ def self.decode_files(raw_tgz)
133
+ raw_files = {}
134
+
135
+ gz_reader = Zlib::GzipReader.new StringIO.new(raw_tgz)
136
+ tar_reader = Archive::Tar::Minitar::Reader.new gz_reader
137
+ tar_reader.each_entry do |entry|
138
+ raise 'Directories unsupported' if entry.directory?
139
+
140
+ name = entry.full_name
141
+ contents = entry.read
142
+ raw_files[name] = contents
143
+ end
144
+ tar_reader.close
145
+ gz_reader.close
146
+
147
+ manifest_file = raw_files.keys.find { |name| /^update.*\.dat$/ =~ name }
148
+ raise 'No update*.dat manifest found' unless manifest_file
149
+
150
+ files = decode_manifest(raw_files[manifest_file])
151
+ files.each { |file| file[:contents] = raw_files[file[:name]] }
152
+ { :name => manifest_file[6...-4], :files => files }
153
+ end
154
+
155
+ # The encoded file data (tgz) for this update.
156
+ def encoded_files
157
+ raw_tgz = ""
158
+ gz_writer = Zlib::GzipWriter.new StringIO.new(raw_tgz)
159
+ tar_writer = Archive::Tar::Minitar::Writer.new gz_writer
160
+
161
+ data = Hash[*@files.map { |file| [file[:name], file[:contents]] }.flatten]
162
+ data["update#{@name}.dat"] = encoded_manifest
163
+ mtime = Time.now.to_i
164
+ data.each do |name, contents|
165
+ tar_writer.add_file_simple(name, :mode => 0100755, :uid => 0, :gid => 0,
166
+ :user => 'root', :group => 'root', :mtime => mtime,
167
+ :size => contents.length) do |file|
168
+ file.write contents
169
+ end
170
+ end
171
+ tar_writer.close
172
+ gz_writer.close
173
+
174
+ raw_tgz
175
+ end
176
+
177
+ # Decodes an update file's manifest (update*.dat).
178
+ #
179
+ # Args:
180
+ # raw_manifest:: a string containing the raw bytes in the manifest file
181
+ #
182
+ # Returns a hash that can be used as the :files key in the options argument to
183
+ # Update's constructor.
184
+ def self.decode_manifest(raw_manifest)
185
+ raw_manifest.split("\n").map do |line|
186
+ target_id, md5, filename, block_count, display_name = *line.split
187
+
188
+ target = TARGET_IDS.keys.find { |k| TARGET_IDS[k].to_s == target_id }
189
+ { :target => target, :name => filename, :display => display_name }
190
+ end
191
+ end
192
+
193
+ # The encoded manifest for this update.
194
+ def encoded_manifest
195
+ @files.map { |file|
196
+ target_id = TARGET_IDS[file[:target]]
197
+ md5 = Digest::MD5.hexdigest file[:contents]
198
+ block_count = file[:contents].length / BLOCK_SIZE[@device]
199
+ [target_id, md5, file[:name], block_count, file[:display]].join ' '
200
+ }.join("\n") + "\n"
201
+ end
202
+
203
+ # Scrambles update bytes (either the MD5 or the TGZ).
204
+ #
205
+ # Args:
206
+ # bytes:: the bytes to be scrambled (in-place)
207
+ # key:: the scrambling key (between 0 and 255)
208
+ #
209
+ # Returns the same array passed in the bytes argument.
210
+ def self.scramble!(bytes, key)
211
+ 0.upto(bytes.length - 1) do |i|
212
+ b = bytes[i]
213
+ b = (((b >> 4) | (b << 4)) & 0xFF) ^ key
214
+ bytes[i] = b
215
+ end
216
+ bytes
217
+ end
218
+
219
+ # Default value for the unknown byte in the update header.
220
+ DEFAULT_UNKNOWN = 0x13
221
+
222
+ # Default value for updates' minimum-allowed version.
223
+ DEFAULT_MIN_VERSION = 0
224
+
225
+ # Default value for updates' maximum-allowed version.
226
+ DEFAULT_MAX_VERSION = 0x7fffffff
227
+
228
+ # Kindle devices ID (numbers in the 3rd and 4th digits of the serial number).
229
+ DEVICE_IDS = {:kindle => 1, :kindle2 => 2, :kindle_dx => 4}
230
+
231
+ # Flash partition block size.
232
+ BLOCK_SIZE = {:kindle => 131072, :kindle2 => 131072, :kindle_dx => 131072 }
233
+
234
+ # Header sizes, based on update types.
235
+ HEADER_SIZES = {:manual => 131072, :ota => 64}
236
+
237
+ # Target IDs in the update manifest.
238
+ TARGET_IDS = { :base_fs => 6, :contents_fs => 7, :temp => 128,
239
+ :exec => 129 }
240
+
241
+ # Update file signatures.
242
+ UPDATE_IDS = {:manual => 'FB01', :ota => 'FC02'}
243
+
244
+ # The key used to scramble update bytes.
245
+ SCRAMBLE_KEY = 0x7A
246
+ end # class
247
+
248
+ end # namespace
@@ -0,0 +1,82 @@
1
+ # Code for dealing with the high-level update file format.
2
+ #
3
+ # Author:: Victor Costan
4
+ # Copyright:: Copyright (C) 2009 Zergling.Net
5
+ # License:: MIT
6
+
7
+ require 'yaml'
8
+
9
+ # :nodoc: namespace
10
+ module KindleHacks
11
+
12
+
13
+ class Update
14
+ def self.read_dir(path)
15
+ manifest = YAML.load File.read(File.join(path, 'update.yml'))
16
+ symbolize_keys! manifest
17
+
18
+ manifest[:files].each do |file|
19
+ file[:contents] = File.read File.join(path, file[:name])
20
+ end
21
+
22
+ Update.new manifest
23
+ end
24
+
25
+ def to_dir(path)
26
+ m = Update.stringify_keys! manifest
27
+ File.open(File.join(path, 'update.yml'), 'w') { |f| YAML.dump m, f }
28
+ @files.each do |file|
29
+ File.open File.join(path, file[:name]), 'w' do |f|
30
+ f.write file[:contents]
31
+ end
32
+ end
33
+ end
34
+
35
+ # The update's manifest.
36
+ def manifest
37
+ m = { :device => @device, :files => @files.map { |f| f.clone },
38
+ :min_version => @min_version, :max_version => @max_version,
39
+ :name => @name, :optional => @optional, :update => @update }
40
+
41
+ m[:files].each do |file|
42
+ file.delete :contents
43
+ file.delete :display if file[:display] == file[:name] + '_file'
44
+ end
45
+ m.delete :min_version if m[:min_version] == DEFAULT_MIN_VERSION
46
+ m.delete :max_version if m[:max_version] == DEFAULT_MAX_VERSION
47
+ m
48
+ end
49
+
50
+ # Recursively converts a hash's keys to symbols.
51
+ #
52
+ # Returns the same hash given as an argument.
53
+ def self.symbolize_keys!(obj)
54
+ if obj.kind_of? Hash
55
+ obj.keys.each do |key|
56
+ value = obj.delete key
57
+ symbolize_keys! value
58
+ obj[key.to_sym] = value
59
+ end
60
+ elsif obj.kind_of? Array
61
+ obj.each { |value| symbolize_keys! value }
62
+ end
63
+ obj
64
+ end
65
+
66
+ # Recursively converts a hash's keys and symbol values to strings.
67
+ def self.stringify_keys!(obj)
68
+ if obj.kind_of? Hash
69
+ obj.keys.each do |key|
70
+ value = obj.delete key
71
+ value = value.to_s if value.kind_of? Symbol
72
+ stringify_keys! value
73
+ obj[key.to_s] = value
74
+ end
75
+ elsif obj.kind_of? Array
76
+ obj.each { |value| stringify_keys! value }
77
+ end
78
+ obj
79
+ end
80
+ end # class
81
+
82
+ end # namespace
@@ -0,0 +1,3 @@
1
+ 128 de22e1808bd84edc219c91cff1ca89fc savoryctl 0 savoryctl_file
2
+ 129 90c7f4645f661c95f3de9893f8492506 001-savory.sh 0 001-savory.sh_file
3
+ 129 f64d5afa016a15ef6046be34067269bb 999-reboot.sh 0 999-reboot.sh_file
@@ -0,0 +1,52 @@
1
+ #!/bin/sh
2
+
3
+ _FUNCTIONS=/etc/rc.d/functions
4
+ [ -f ${_FUNCTIONS} ] && . ${_FUNCTIONS}
5
+
6
+ . /etc/sysconfig/mntus
7
+
8
+ VERSION_FILE=/etc/prettyversion.txt
9
+ SAVE_PRETTY=${VERSION_FILE}-beforesavory
10
+ NF_INI=/opt/amazon/ebook/config/netfront.ini
11
+ SAVE_NF_INI=${NF_INI}-beforesavory
12
+ SAVORYCTL=/etc/init.d/savoryctl
13
+
14
+ # If savory is already there, bail out
15
+ if [ -f $SAVORYCTL ]; then
16
+ update_progressbar 90
17
+ return 0
18
+ fi
19
+
20
+
21
+ update_progressbar 20
22
+
23
+ cp savoryctl $SAVORYCTL
24
+ chown root.root $SAVORYCTL
25
+ chmod 755 $SAVORYCTL
26
+ ln -s $SAVORYCTL /etc/rc5.d/S99savoryctl
27
+
28
+ update_progressbar 50
29
+
30
+
31
+ if [ -f $NF_INI ]; then
32
+ cp $NF_INI $SAVE_NF_INI
33
+ sed 's/dlexts=prc mobi txt azw$/dlexts=prc mobi txt azw epub lrf lit pdf fb2 odt/' $SAVE_NF_INI > /tmp/netfront-temp
34
+ sed 's/dlmimes=application\/x-mobipocket-ebook text\/x-prc$/dlmimes=application\/x-mobipocket-ebook text\/x-prc application\/pdf/' /tmp/netfront-temp > /tmp/netfront-temp-2
35
+
36
+ if [ -s /tmp/netfront-temp-2 ]; then
37
+ cp /tmp/netfront-temp-2 $NF_INI
38
+ fi
39
+
40
+ fi
41
+
42
+ update_progressbar 60
43
+
44
+
45
+ cp $VERSION_FILE $SAVE_PRETTY
46
+ sed 's/$/ UNSUPPORTED SAVORY-0.06/' $SAVE_PRETTY > $VERSION_FILE
47
+
48
+ update_progressbar 100
49
+
50
+ return 0
51
+
52
+
@@ -0,0 +1,18 @@
1
+ #!/bin/sh
2
+
3
+ _FUNCTIONS=/etc/rc.d/functions
4
+ [ -f ${_FUNCTIONS} ] && . ${_FUNCTIONS}
5
+
6
+ . /etc/sysconfig/mntus
7
+
8
+ rm -f ${MNTUS_MP}/update*.bin
9
+
10
+ display_update_screen_success
11
+
12
+ sync
13
+ sleep 10
14
+
15
+ /sbin/reboot
16
+
17
+ return 0
18
+
@@ -0,0 +1,35 @@
1
+ #!/bin/sh
2
+ PATH=/tmp/savory/bin:$PATH
3
+ NAME="savory file conversion daemon"
4
+ DAEMON=/tmp/savory/bin/savory_daemon
5
+ PIDFILE=/var/run/savory-daemon.pid
6
+ _FUNCTIONS=/etc/rc.d/functions
7
+ [ -f ${_FUNCTIONS} ] && . ${_FUNCTIONS}
8
+
9
+ case "$1" in
10
+
11
+ start)
12
+ mkdir /tmp/savory
13
+ mount -o loop -o ro /mnt/us/system/savory-image.ext3 /tmp/savory
14
+ if [ -x "$DAEMON" ]; then
15
+ msg "starting $NAME" I
16
+ $DAEMON -p $PIDFILE &
17
+ fi
18
+ ;;
19
+
20
+ stop)
21
+ if [ -r "$PIDFILE" ]; then
22
+ msg "stopping $NAME" I
23
+ kill `cat $PIDFILE`
24
+ rm -f $PIDFILE
25
+ umount /tmp/savory
26
+ fi
27
+ ;;
28
+ *)
29
+ msg "Usage: /etc/init.d/$NAME {start|stop}" W >&2
30
+ exit 1
31
+ ;;
32
+ esac
33
+
34
+ exit 0
35
+
@@ -0,0 +1,12 @@
1
+ ---
2
+ name: _Savory-0.06
3
+ device: kindle2
4
+ optional: 0
5
+ files:
6
+ - name: savoryctl
7
+ target: temp
8
+ - name: 001-savory.sh
9
+ target: exec
10
+ - name: 999-reboot.sh
11
+ target: exec
12
+ update: ota
@@ -0,0 +1,124 @@
1
+ # Author:: Victor Costan
2
+ # Copyright:: Copyright (C) 2009 Zergling.Net
3
+ # License:: MIT
4
+
5
+ require 'kindle_hacks'
6
+ require 'fileutils'
7
+ require 'test/unit'
8
+
9
+ class UpdateTest < Test::Unit::TestCase
10
+ Update = KindleHacks::Update
11
+
12
+ def setup
13
+ @fixtures_path = File.join(File.dirname(__FILE__), 'fixtures')
14
+ @update_path = File.join(@fixtures_path, 'update')
15
+
16
+ @kindle2_update = File.read File.join(@fixtures_path, 'update_k2_pdf.bin')
17
+ @kindledx_update = File.read File.join(@fixtures_path, 'update_dx_web.bin')
18
+
19
+ @manifest = File.read File.join(@fixtures_path, 'update.dat')
20
+
21
+ FileUtils.mkdir_p 'test_tmp'
22
+ end
23
+
24
+ def teardown
25
+ FileUtils.rm_r 'test_tmp'
26
+ end
27
+
28
+ def test_header_encoding_decoding
29
+ k2_golden = { :optional => 0, :update => :ota, :device => :kindle2,
30
+ :unknown => Update::DEFAULT_UNKNOWN,
31
+ :min_version => Update::DEFAULT_MIN_VERSION,
32
+ :max_version => Update::DEFAULT_MAX_VERSION }
33
+ dx_golden = { :optional => 0, :update => :ota, :device => :kindle_dx,
34
+ :unknown => Update::DEFAULT_UNKNOWN,
35
+ :min_version => Update::DEFAULT_MIN_VERSION,
36
+ :max_version => Update::DEFAULT_MAX_VERSION }
37
+
38
+ assert_equal k2_golden, Update.decode_header(@kindle2_update),
39
+ 'decoding kindle2_pdf'
40
+ assert_equal dx_golden, Update.decode_header(@kindledx_update),
41
+ 'decoding kindle_dx_web'
42
+
43
+ assert_equal @kindle2_update[0, 16], Update.new(k2_golden).encoded_header,
44
+ 'encoding kindle2_pdf'
45
+ assert_equal @kindledx_update[0, 16], Update.new(dx_golden).encoded_header,
46
+ 'encoding kindle_dx_pdf'
47
+ end
48
+
49
+ def test_decode_manifest
50
+ golden = [{ :display => "savoryctl_file", :name => "savoryctl",
51
+ :target => :temp },
52
+ { :display => "001-savory.sh_file", :name => "001-savory.sh",
53
+ :target => :exec },
54
+ { :display => "999-reboot.sh_file", :name => "999-reboot.sh",
55
+ :target => :exec }]
56
+ assert_equal golden, Update.decode_manifest(@manifest), 'decoding manifest'
57
+ end
58
+
59
+ def test_read
60
+ update = Update.read @kindle2_update
61
+ assert_equal :kindle2, update.device, 'Update device'
62
+ assert_equal '_Savory-0.06', update.name, 'Update name'
63
+ assert_equal :ota, update.update, 'Update update (type)'
64
+ update.files.each do |file|
65
+ golden = File.read File.join(@update_path, file[:name])
66
+ assert_equal "#{file[:name]}_file", file[:display],
67
+ "File #{file[:name]} display name"
68
+ assert_equal golden, file[:contents], "File #{file[:name]} contents"
69
+ end
70
+ end
71
+
72
+ def test_manifest
73
+ golden_file = File.read File.join(@update_path, 'update.yml')
74
+
75
+ golden = Update.symbolize_keys! YAML.load(golden_file)
76
+ golden[:device] = golden[:device].to_sym
77
+ golden[:update] = golden[:update].to_sym
78
+ golden[:files].each { |f| f[:target] = f[:target].to_sym }
79
+
80
+ assert_equal golden, Update.read(@kindle2_update).manifest
81
+ end
82
+
83
+ def test_to_dir
84
+ update = Update.read @kindle2_update
85
+ update.to_dir 'test_tmp'
86
+
87
+ Dir.entries(@update_path).each do |entry|
88
+ next if File.directory?(entry)
89
+
90
+ assert File.exist?(File.join('test_tmp', entry)),
91
+ "Serialized update doesn't have #{entry}"
92
+ assert_equal File.read(File.join(@update_path, entry)),
93
+ File.read(File.join('test_tmp', entry)),
94
+ "Bad contents in serialized update's #{entry}"
95
+ end
96
+ end
97
+
98
+ def test_read_dir
99
+ golden_update = Update.read @kindle2_update
100
+ update = Update.read_dir @update_path
101
+
102
+ assert_equal golden_update.manifest, update.manifest
103
+ assert_equal golden_update.files, update.files
104
+ end
105
+
106
+ def test_encoded_manifest
107
+ update = Update.read @kindle2_update
108
+ assert_equal @manifest, update.encoded_manifest
109
+ end
110
+
111
+ def test_to_binary
112
+ golden_update = Update.read @kindle2_update
113
+ binary = golden_update.to_binary
114
+ update = Update.read binary
115
+
116
+ assert_equal golden_update.manifest, update.manifest
117
+ assert_equal golden_update.files, update.files
118
+ end
119
+
120
+ def test_binary_file_name
121
+ update = Update.read @kindle2_update
122
+ assert_equal "update_Savory-0.06.bin", update.binary_file_name
123
+ end
124
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kindle_hacks
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors:
7
+ - Victor Costan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-09 00:00:00 -04:00
13
+ default_executable: bin/kindle_update
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: archive-tar-minitar
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.5.2
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: echoe
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 3.1.1
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: flexmock
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.8.6
44
+ version:
45
+ description: Assorted toolbox for hacking into Kindles.
46
+ email: victor@zergling.net
47
+ executables:
48
+ - kindle_update
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - CHANGELOG
53
+ - LICENSE
54
+ - README.textile
55
+ - bin/kindle_update
56
+ - lib/kindle_hacks.rb
57
+ - lib/kindle_hacks/update.rb
58
+ - lib/kindle_hacks/update_hl.rb
59
+ files:
60
+ - CHANGELOG
61
+ - LICENSE
62
+ - Manifest
63
+ - README.textile
64
+ - Rakefile
65
+ - bin/kindle_update
66
+ - lib/kindle_hacks.rb
67
+ - lib/kindle_hacks/update.rb
68
+ - lib/kindle_hacks/update_hl.rb
69
+ - test/fixtures/update.dat
70
+ - test/fixtures/update/001-savory.sh
71
+ - test/fixtures/update/999-reboot.sh
72
+ - test/fixtures/update/savoryctl
73
+ - test/fixtures/update/update.yml
74
+ - test/fixtures/update_dx_web.bin
75
+ - test/fixtures/update_k2_pdf.bin
76
+ - test/update_test.rb
77
+ - kindle_hacks.gemspec
78
+ has_rdoc: true
79
+ homepage: http://github.com/costan/kindle_hacks
80
+ licenses: []
81
+
82
+ post_install_message:
83
+ rdoc_options:
84
+ - --line-numbers
85
+ - --inline-source
86
+ - --title
87
+ - Kindle_hacks
88
+ - --main
89
+ - README.textile
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: "0"
97
+ version:
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: "1.2"
103
+ version:
104
+ requirements: []
105
+
106
+ rubyforge_project: zerglings
107
+ rubygems_version: 1.3.5
108
+ signing_key:
109
+ specification_version: 3
110
+ summary: Assorted toolbox for hacking into Kindles.
111
+ test_files:
112
+ - test/update_test.rb