[sane-devel] USB recordings MD6190 available
Lauri Pirttiaho
lauri.pirttiaho at luukku.com
Mon Sep 26 18:25:44 UTC 2005
Hello,
Bertrik Sikken on Sun Sep 25 20:35:55 UTC 2005
>Lauri Pirttiaho wrote:
>> -- value 0080 returns a version number string
>
>Hmm, haven't seen this one in the logs.
That is true. I checked the medion firmware value interpreter
table and it does not contain value 0080. That seems to be
specific to CanoScan 3200F. As I suspected, the two versions
of the firmware may not be compatible.
>> -- value 0040 returns state of a state machine driven by ie1 as its
>> first byte and I suspect that state machine to be the motor control.
>> The second byte is port 1 bit 0 which I suspect to be he head home
>> detector. Additionally there are 3 more bytes of info (I con't
>> know the meaning of these yet), 0xAA and then 0's.
>
>I noticed some increasing number at bytes 2 and 3 and suspect that
>this is the current line number of the carriage.
That, too, may be different from the CanoScan one...
>> -- There is some kind of clocked serial device connecter to processor
>> port 0 bits 5-7 -- it may be an EEPROM but maybe also something else.
>
>Wow, you opened up the device and attached a 'scope?
Too expensive... :) I just read the code.
>> That is being worked on but proceeds slowly, about 1 hour per night...
>
>I made a start with the datasheet on Martin's wiki page.
Very excellent. I will contribute as soon as I get some
definite out. However, the CanoScan info may not always
apply to the Medion when the firmware is considered but
data about the ALi chip probably is valid for both.
>> If someone wants to help with reading the firmware I can provide
>> more details about tools to do the disassembly and code analysis.
>> It would help if someone decompiled the Medion version of the
>> firmware (doing manual detection of the C-compiler emit patterns
>> in the assembly code or write a tool to do that automatically).
>
>I can have a try at that. I don't have any experience with the 8032
>but I do have experience with other (similar) processors.
The first doc to consult is the MCS51 Microcontroller Family User's
Manual still available from Intel, listedon page
http://www.intel.com/design/mcs51/docs_mcs51.htm
and the direct link to pdf download page is
http://www.intel.com/design/mcs51/manuals/272383.htm
The second must is DIS51, an excellent and straight forward
disassembler for MCS51 available from
http://home.earthlink.net/~davesullins/software/dis51.html
The disassembler requires some manual support to tell the
addresses from where to start the disassembly. Then it walks
through all code reachable from those addresses. Obvious
firts addresses are the reset and interrupt vectors. Additionally
one has to manually dig out the places to which JMP @A+DPTR
instructions go and kill some dead ends left by quirks like
grabbing the return address of LCALL for DPTR (as used in the command
interpreter tables, I replace the byte following the LCALL
by RET and then after disassembly put back the original value).
DIS51 eats intel hex so another must is objcopy which is part
of gnu binutils and comes with Cygwin ("Don't use your
windows without it!" (tm)). Objcopy can convert the binary
dump from the USB log to ihex: objcopy -Ibinary -Oihex file.bin file.hex.
This is what my do_dis51 script looks like for the Medion file:
-- snip --
#!bash
objcopy -Ibinary -Oihex medion_fw_mod.bin medion_fw_mod.hex
../dis51-0.5/dis51 -l 0 3 11 19 7885 7888 7891 7894 7897 7900 \
7903 7906 7909 7912 7915 7918 7921 7924 7927 7930 7933 7936 7939 \
7942 7945 7948 7951 7954 7957 7960 7963 7966 7969 7972 7975 7978 \
7981 7984 7987 7990 7993 7996 7999 8002 8005 8008 8011 26092 26087 \
26124 26119 26099 26104 26109 26114 26127 27165 27172 27135 27130 \
27209 27204 27140 27189 27184 27179 27145 27150 27194 27155 27160 \
27234 27229 27239 27244 27224 27219 27214 27199 27403 27250 27325 \
27329 27379 27320 27374 27384 27389 27336 27344 27349 27315 27369 \
27354 27359 27364 27394 27403 27400 < medion_fw_mod.hex > medion_fw_mod.asm
-- snip --
>From there on it is just reading the assembly and trying to make
sense of it. It starts at 0 and first in L0001 initializes
low memory and stack pointer. Then it executes L0003 which obviously
is a c_init. When done it goes to L0015 which is main()
and the first call there, L0016 is user supplied initialization
at the beginning of main(). Rest of the main is just event loop
driven by a dozen event bits (yes, exactly 12!). The events are
generated e.g. by interrupts. Ie0 handler L0401 just masks all
USB interrupts, puts the reason to address 53h and fires event
23h, the first one in the event loop. That reads the USB packets
and fires other events as necessary. Actual command interpreter
is fired by fall through in the event loop (that is event 2Ah).
Ie1 handler L0418 is a huge state machine I suspect to be the
motor controller for the scan head movement. The timer 0 interrupt
is still mystery but that uses the serial device in port 0
(see L0333).
Sometimes it helps to see the program flow visually and
for that I have a tool that uses dot available from
http://www.graphviz.org/ ("Don't make graphs without it!"
(tm)). The tool is a perl script that looks like
the following:
-- snip ---
#!perl
# pflow.pl [initial_node] [[excluded subs]...] < [asm_listing] > [dot file]
$init_node = "L0001";
if ( exists $ARGV[0] ) {
if ( $ARGV[0] eq "-h" || $ARGV[0] eq "-H" || $ARGV[0] eq "--help " ) {
print <<EOHELP;
pflow.pl -h|-H|--help
print this help
pflow.pl [initial_node] [-x [exclude_subs]...] [-t [terminate_point]...]
create flow starting at [initial_node] excluding calls to addresses
listed at [xclude_subs]... terminating flow at labels [terminate_point]...
EOHELP
die 0;
} else {
if ( $ARGV[0] ne "-x" && $ARGV[0] ne "-t" ) {
$init_node = shift @ARGV;
}
}
}
$dummynode = 0;
if ( exists $ARGV[0] && $ARGV[0] eq "-x" ) {
shift @ARGV;
while ( exists $ARGV[0] && $ARGV[0] ne "-t" ) {
push @exclude, shift @ARGV;
}
}
if ( exists $ARGV[0] && $ARGV[0] eq "-t" ) {
shift @ARGV;
while ( exists $ARGV[0] ) {
push @terminate, shift @ARGV;
}
}
$has_label = 0;
$node_in = 0;
$node_out = 0;
$node_term = 0;
$in_flow = 0;
$previous_label = "none";
sub print_flow;
while ( <> ) {
if ( /^(L[[:digit:]]{4})/ ) {
$has_label = 1;
$node_in = 1;
$label_in = $1;
next;
}
if ( /^ ([[:xdigit:]]{4})/ &&
$has_label == 0 ) {
$label_in = "A$1";
}
$has_label = 0;
if ( /^[[:space:]]{2}[[:xdigit:]]{4}[[:space:]][[:xdigit:]]+[[:space:]]+([[:upper:]]+)[[:space:]]/ ) {
$cmd = $1;
$cmd_class = 0;
if ( $cmd eq "JC" ||
$cmd eq "JNC" ||
$cmd eq "JB" ||
$cmd eq "JNB" ||
$cmd eq "JBC" ||
$cmd eq "JZ" ||
$cmd eq "JNZ" ||
$cmd eq "CJNE" ||
$cmd eq "DJNZ" ) {
$cmd_class = 1;
}
if ( $cmd eq "ACALL" ||
$cmd eq "LCALL" ) {
$cmd_class = 2;
}
if ( $cmd eq "RET" ||
$cmd eq "RETI" ) {
$cmd_class = 3;
}
if ( $cmd eq "AJMP" ||
$cmd eq "LJMP" ||
$cmd eq "SJMP" ) {
$cmd_class = 4;
}
if ( $cmd eq "JMP" ) {
$cmd_class = 5;
}
if ( $cmd_class != 0 ) {
$node_out = 1;
}
if ( $cmd_class == 3 ||
$cmd_class == 4 ||
$cmd_class == 5 ) {
$node_term = 1;
}
# print ">>$1 $cmd_class $node_out $node_term\n";
}
if ( /(L[[:digit:]]{4})$/ ) {
$label_out = $1;
$node_out = 1;
}
if ( $cmd_class == 5 ) {
$label_out = "UNKNOWN";
}
if ( $cmd_class == 3 ) {
$label_out = "X";
}
if ( $cmd_class == 2 ) {
$label_out = "Call_" . $label_out;
}
if ( $in_flow && ( $node_in || $node_out ) ) {
# print "$previous_label -> $label_in;\n";
push @{$links{$previous_label}},$label_in;
$previous_label = $label_in;
}
if ( !$in_flow && $node_in ) {
$in_flow = 1;
$previous_label = $label_in;
}
if ( $in_flow && $node_out ) {
# print "$label_in -> $label_out;\n";
push @{$links{$label_in}},$label_out;
$previous_label = $label_in;
}
if ( $node_term ) {
$in_flow = 0;
$previous_label = "none";
}
$node_in = 0;
$node_out = 0;
$node_term = 0;
}
print "digraph {\n";
print "ratio=compress;\n";
print "size=\"8.25,10.2\";\n";
print "$init_node [ shape = box ];\n";
print_flow $init_node, "End_$init_node";
print "}";
# for $a ( sort(keys(%links)) ) {
# for $b ( sort(@{$links{$a}}) ) {
# print "$a -> $b;\n";
# }
# }
sub print_flow {
my $a = shift( @_ );
my $e = shift( @_ );
if ( exists $links{$a} ) {
my $bl = delete $links{ $a };
for my $b ( sort @{$bl} ) {
if ( $b eq "X" ) {
$b = $e;
print "$b [ shape = house ];\n";
}
if ( $b =~ /Call_(L[[:digit:]]{4})/ ) {
print "$a -> Dummy_$dummynode;\n";
print "Dummy_$dummynode [ label = \"$b\", shape = hexagon ]";
$dummynode++;
$include = 1;
for my $l ( @exclude ) {
$include = 0 if $1 eq $l;
}
if ( $include ) {
print "$1 [ shape = invhouse ];\n";
print "subgraph {\n";
print_flow $1, "Return_$1";
print "}\n";
}
} else {
$continue = 1;
print "$a -> $b\n";
for my $l ( @terminate ) {
$continue = 0 if $b eq $l;
}
if ( $continue ) {
print_flow $b,$e;
} else {
print "$b [ shape = box ];\n";
}
}
}
}
}
--- snip ---
And can be used like following:
--- snip ---
#!bash
./pflow.pl L0016 <medion_fw_mod.asm | dot -Tps2 | ps2pdf - L0016.pdf
--- snip ---
Great? And ps2pdf comes with Cygwin, naturally...
Happy reversing to anyone who want's to try!
With best regards,
Lauri Pirttiaho
Oulu
Finland
...................................................................
Luukku Plus paketilla pääset eroon tila- ja turvallisuusongelmista.
Hanki Luukku Plus ja helpotat elämääsi. http://www.mtv3.fi/luukku
More information about the sane-devel
mailing list