Perl-generated UTF8 strings in JSON corrupted on client side - json

I have a Perl CGI script that is accessing Thai language, UTF-8 strings from a PostgreSQL DB and returning them to a web-based front end as JSON. The strings are fine when I get them from the DB and after I encode them as JSON (based on writing to a log file). However, when the client receives them they are corrupted, for example:
featurename "à¹\u0082รà¸\u0087à¹\u0080รียà¸\u0099วัà¸\u0094ภาษี"
Clearly some chars are being converted to Unicode escape sequences, but not all.
I could really use some suggestions as to how to solve this.
Simplified code snippet follows. I am using 'utf8' and 'utf8::all', as well as 'JSON'.
Thanks in advance for any help you can provide.
my $dataId = $cgi->param('dataid');
my $table = "uploadpoints";
my $sqlcommand = "select id,featurename from $table where dataid=$dataId;";
my $stmt = $gDbh->prepare($sqlcommand);
my $numrows = $stmt->execute;
# print JSON header
print <<EOM;
Content-type: application/json; charset="UTF-8"
EOM
my #retarray;
for (my $i = 0; ($i < $numrows); $i=$i+1)
{
my $hashref = $stmt->fetchrow_hashref("NAME_lc");
#my $featurename = $hashref->{'featurename'};
#logentry("Point $i feature name is: $featurename\n");
push #retarray,$hashref;
}
my $json = encode_json (\#retarray);
logentry("JSON\n $json");
print $json;
I have modified and simplified the example, now running locally rather than via browser invocation:
my $dataId = 5;
my $table = "uploadpoints";
my $sqlcommand = "select id,featurename from $table where dataid=$dataId and id=75;";
my $stmt = $gDbh->prepare($sqlcommand);
my $numrows = $stmt->execute;
my #retarray;
for (my $i = 0; ($i < $numrows); $i=$i+1)
{
my $hashref = $stmt->fetchrow_hashref("NAME_lc");
my $featurename = $hashref->{'featurename'};
print "featurename $featurename\n";
push #retarray,$hashref;
}
my $json = encode_json (\#retarray);
print $json;
Using hexdump as in Stefan's example, I've determined that the data as read from the database are already in UTF-8. It looks as though they are being re-encoded in the JSON encode method. But why?
The data in the JSON use exactly twice as many bytes as the original UTF-8.
perl testcase.pl | hexdump -C
00000000 66 65 61 74 75 72 65 6e 61 6d 65 20 e0 b9 82 e0 |featurename ....|
00000010 b8 a3 e0 b8 87 e0 b9 80 e0 b8 a3 e0 b8 b5 e0 b8 |................|
00000020 a2 e0 b8 99 e0 b9 81 e0 b8 88 e0 b9 88 e0 b8 a1 |................|
00000030 e0 b8 88 e0 b8 b1 e0 b8 99 e0 b8 97 e0 b8 a3 e0 |................|
00000040 b9 8c 0a 5b 7b 22 66 65 61 74 75 72 65 6e 61 6d |...[{"featurenam|
00000050 65 22 3a 22 c3 a0 c2 b9 c2 82 c3 a0 c2 b8 c2 a3 |e":"............|
00000060 c3 a0 c2 b8 c2 87 c3 a0 c2 b9 c2 80 c3 a0 c2 b8 |................|
00000070 c2 a3 c3 a0 c2 b8 c2 b5 c3 a0 c2 b8 c2 a2 c3 a0 |................|
00000080 c2 b8 c2 99 c3 a0 c2 b9 c2 81 c3 a0 c2 b8 c2 88 |................|
00000090 c3 a0 c2 b9 c2 88 c3 a0 c2 b8 c2 a1 c3 a0 c2 b8 |................|
000000a0 c2 88 c3 a0 c2 b8 c2 b1 c3 a0 c2 b8 c2 99 c3 a0 |................|
000000b0 c2 b8 c2 97 c3 a0 c2 b8 c2 a3 c3 a0 c2 b9 c2 8c |................|
000000c0 22 2c 22 69 64 22 3a 37 35 7d 5d |","id":75}]|
000000cb
Further suggestions? I tried using decode on the UTF string but got errors related to wide characters.
I did read the recommended answer from Tom Christianson, as well as his Unicode tutorials, but I will admit much of it went over my head. Also it seems my problem is considerably more constrained.
I did wonder whether retrieving the hash value and assigning it to a normal variable was doing some sort of auto-decoding or encoding. I do not really understand when Perl uses its internal character format as opposed to when it retains the external encoding.
UPDATE WITH SOLUTION
Turns out that since the string retrieved from the DB is already in UTF-8, I need to use 'to_json' rather than 'encode_json'. This fixed the problem. Learned a lot about Perl Unicode handling in the process though...
Also recommend: http://perldoc.perl.org/perluniintro.html
Very clear exposition.

NOTE: you should probably also read this answer, which makes my answer sub-par in comparison :-)
The problem is that you have to be sure in which format each string is, otherwise you'll get incorrect conversions. When handling UTF-8 a string can be in two formats:
raw UTF-8 encoded octet string, i.e. \x{100} represented as two octets 0xC4 0x80
internal Perl string representation, i.e. one Unicode character \x{100} (U+0100 Ā LATIN CAPITAL LETTER A WITH MACRON)
If I/O is involved you also need to know if the I/O layer does UTF-8 de/encoding or not. For terminal I/O you also have to consider if it understands UTF-8 or not. Both taken together can make it difficult to get meaningful debug printouts from your code.
If you Perl code needs to process UTF-8 strings after reading them from the source, you must make sure that they are in internal Perl format. Otherwise you'll get surprising result when you call code that expects Perl strings and not raw octet strings.
I try to show this in my example code:
#!/usr/bin/perl
use warnings;
use strict;
use JSON;
open(my $utf8_stdout, '>& :encoding(UTF-8)', \*STDOUT)
or die "can't reopen STDOUT as utf-8 file handle: $!\n";
my $hex = "C480";
print "${hex}\n";
my $raw = pack('H*', $hex);
print STDOUT "${raw}\n";
print $utf8_stdout "${raw}\n";
my $decoded;
utf8::decode($decoded = $raw);
print STDOUT ord($decoded), "\n";
print STDOUT "${decoded}\n"; # Wide character in print at...
print $utf8_stdout "${decoded}\n";
my $json = JSON->new->encode([$decoded]);
print STDOUT "${json}\n"; # Wide character in print at...
print $utf8_stdout "${json}\n";
$json = JSON->new->utf8->encode([$decoded]);
print STDOUT "${json}\n";
print $utf8_stdout "${json}\n";
exit 0;
Copy & paste from my terminal (which supports UTF-8). Look closely at the differences between the lines:
$ perl dummy.pl
C480
Ā
Ä
256
Wide character in print at dummy.pl line 21.
Ā
Ā
Wide character in print at dummy.pl line 25.
["Ā"]
["Ā"]
["Ā"]
["Ä"]
But compare this to the following, where STDOUT is not a terminal, but piped to another program. The hex dump always shows "c4 80", i.e. UTF-8 encoded.
$ perl dummy.pl | hexdump -C
Wide character in print at dummy.pl line 21.
Wide character in print at dummy.pl line 22.
Wide character in print at dummy.pl line 25.
Wide character in print at dummy.pl line 26.
00000000 43 34 38 30 0a c4 80 0a c4 80 0a 5b 22 c4 80 22 |C480.......[".."|
00000010 5d 0a 5b 22 c4 80 22 5d 0a 43 34 38 30 0a c4 80 |].[".."].C480...|
00000020 0a 32 35 36 0a c4 80 0a 5b 22 c4 80 22 5d 0a 5b |.256....[".."].[|
00000030 22 c4 80 22 5d 0a |".."].|
00000036

Related

ASC 122U NFC Reader compatible with EMV Contactless Cards

I am trying to read a EMV card using an APDU Command, however it seems the ACR122U external reader is blocking the APDU Command.
Select Command:
APDU-C -> 00 A4 04 00 0E 32 50 41 59 2E 53 59 53 2E 44 44 46 30 31 0E
APDU-R <- Error no response
Is it possible that the ACR122U reader is blocking the command ?
You want to SELECT FILE 1PAY.SYS.DDF01,
"Payment System Environment (PSE)"
To get the PSE directory and the card should response with Application Identifier (AID). but you set the LE=0E replace it to "00" .
Corrected APDU =>
PPSE = '00 a4 04 00 0e 32 50 41 59 2e 53 59 53 2e 44 44 46 30 31 00'
if The selection failed then the ADF doesn't exist (SW1/SW2=6A82)
if the selection is true then Application Identifier (AID) start command
Possible AID's:
A0000000031010
A0000000032020
A0000000041010
A0000000043060
AIDPrefix ='00 a4 04 00 07'

Passing Parameters in Assembly

I have this question that I need help on. I'm supposed to detect the password (the answer) to this problem which is one of the parameters in assembly.
Output:
In this level, you will need to use gdb to find the password as it is
being passed as a parameter to a function with 6 parameters. For x86-64,
the mnemonic for passing parameters using registers is Diane's Silk Dress
Cost $89. (rdi rsi rdx rcx r8 r9)
Enter the password:
The objdump code of the function I believe has the 6 parameters --
00000000004006a1 <foo>:
4006a1: 55 push %rbp
4006a2: 48 89 e5 mov %rsp,%rbp
4006a5: 53 push %rbx
4006a6: 48 83 ec 48 sub $0x48,%rsp
4006aa: 48 89 7d d8 mov %rdi,-0x28(%rbp)
4006ae: 48 89 75 d0 mov %rsi,-0x30(%rbp)
4006b2: 48 89 55 c8 mov %rdx,-0x38(%rbp)
4006b6: 48 89 4d c0 mov %rcx,-0x40(%rbp)
4006ba: 4c 89 45 b8 mov %r8,-0x48(%rbp)
4006be: 4c 89 4d b0 mov %r9,-0x50(%rbp)
4006c2: c7 45 e4 01 00 00 00 movl $0x1,-0x1c(%rbp)
4006c9: 48 8b 45 c0 mov -0x40(%rbp),%rax
4006cd: 48 89 c7 mov %rax,%rdi
4006d0: e8 5b fe ff ff callq 400530 <strlen#plt>
4006d5: 89 45 ec mov %eax,-0x14(%rbp)
4006d8: 8b 45 ec mov -0x14(%rbp),%eax
4006db: 48 63 d8 movslq %eax,%rbx
4006de: 48 8b 45 b0 mov -0x50(%rbp),%rax
4006e2: 48 89 c7 mov %rax,%rdi
4006e5: e8 46 fe ff ff callq 400530 <strlen#plt>
4006ea: 48 39 c3 cmp %rax,%rbx
4006ed: 74 07 je 4006f6 <foo+0x55>
4006ef: c7 45 e4 00 00 00 00 movl $0x0,-0x1c(%rbp)
4006f6: c7 45 e8 00 00 00 00 movl $0x0,-0x18(%rbp)
4006fd: eb 31 jmp 400730 <foo+0x8f>
4006ff: 8b 45 e8 mov -0x18(%rbp),%eax
400702: 48 63 d0 movslq %eax,%rdx
400705: 48 8b 45 b8 mov -0x48(%rbp),%rax
400709: 48 01 d0 add %rdx,%rax
40070c: 0f b6 10 movzbl (%rax),%edx
40070f: 8b 45 e8 mov -0x18(%rbp),%eax
400712: 48 63 c8 movslq %eax,%rcx
400715: 48 8b 45 b0 mov -0x50(%rbp),%rax
400719: 48 01 c8 add %rcx,%rax
40071c: 0f b6 00 movzbl (%rax),%eax
40071f: 38 c2 cmp %al,%dl
400721: 74 09 je 40072c <foo+0x8b>
400723: c7 45 e4 00 00 00 00 movl $0x0,-0x1c(%rbp)
40072a: eb 0c jmp 400738 <foo+0x97>
40072c: 83 45 e8 01 addl $0x1,-0x18(%rbp)
400730: 8b 45 e8 mov -0x18(%rbp),%eax
400733: 3b 45 ec cmp -0x14(%rbp),%eax
400736: 7c c7 jl 4006ff <foo+0x5e>
400738: 8b 45 e4 mov -0x1c(%rbp),%eax
40073b: 48 83 c4 48 add $0x48,%rsp
40073f: 5b pop %rbx
400740: 5d pop %rbp
400741: c3 retq
I believe from 4006aa to 4006be is the parameters. However, when I entered the values of those registers one by one individually as the answer, it said it wasn't the right answer (ex, -0x28). I tried inputting the answers in decimal, hex, and binary. I used gdb to debug it and print out the 6 parameter registers in the function and use those values gotten as the answer individually, it didn't work either. I'm stuck. Can I have some guidance on how to go about this problem?
note that I don't have access to the c code for this problem, or the assembly code written by a human. Only the objdump code, and the executable.
Here's the executable that I was given for this if you guys want to try it yourself to be able to help me out: https://www.mediafire.com/file/uglm044vw87lb11/ParamsRegs
4006e5: callq 400530 <strlen#plt> calls a function to determine the length of a string. It uses the same calling convention as your function, taking a const char * arg in RDI. It's set here, from your 4th function arg:
4006b6: mov %rcx,-0x40(%rbp)
4006c9: mov -0x40(%rbp),%rax
4006cd: mov %rax,%rdi
This C standard library function returns the size of the string in rax. Your function takes the low 32 bits of it and sign-extends that into rbx (presumably the source did int len = strlen(arg4);):
4006d5: mov %eax,-0x14(%rbp)
4006d8: mov -0x14(%rbp),%eax
4006db: movslq %eax,%rbx
At 4006de to 4006e5 the same procedure is done again with another string set at 4006be: mov %r9,-0x50(%rbp) and after this second call, the function compares the lengths:
4006ea: cmp %rax,%rbx
my guess is, that one of this strings is the password you are looking for and the other one is the string you put in. Just pause the execution at 4006a1, print the registers with info registers, look at rcx and r9 with x/s <value in rcx/r9>.
Or if the caller of this function parses your input into multiple string args, maybe with scanf, then those two words need to be the same length.

entry0 meaning in radare2

I'm new to binary analysis. I am trying to analyse a simple program I compiled from my C code via gcc.
I followed these steps:
1. aaa
2. afl
and I got this output:
0x00000608 3 23 sym._init
0x00000630 1 8 sym.imp.puts
0x00000638 1 8 sym.imp._IO_getc
0x00000640 1 8 sym.imp.__printf_chk
0x00000648 1 8 sym.imp.__cxa_finalize
0x00000650 4 77 sym.main
0x000006a0 1 43 entry0
0x000006d0 4 50 -> 44 sym.deregister_tm_clones
0x00000710 4 66 -> 57 sym.register_tm_clones
0x00000760 5 50 sym.__do_global_dtors_aux
0x000007a0 4 48 -> 42 sym.frame_dummy
0x000007d0 1 24 sym.smth
0x000007f0 4 101 sym.__libc_csu_init
0x00000860 1 2 sym.__libc_csu_fini
0x00000864 1 9 sym._fini
I can get main is the main starting point of the program but I'm worried about what entry0 is. Apparently from what I saw is not a symbol. I tried to run ag # entry0 and ag # main and the graphs I saw were very different. By looking at the disassembled code I see this for entry0:
I'm supposing this might be a kind of ELF template function to load the binary and run it from main. What is entry0 really?
Sorry for keeping it so long. Thanks in advance.
You should post RE questions on https://reverseengineering.stackexchange.com/.
entry0 is an alias for the _start symbol, which corresponds to the _start function.
The memory address of _start is the program entry point, where control is passed from the loader to the program.
The _start function originates from a relocatable ELF object file called crt1.o that is linked into binaries that require the C runtime environment.
$ objdump -dj .text /usr/lib/x86_64-linux-gnu/crt1.o
/usr/lib/x86_64-linux-gnu/crt1.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <_start>:
0: 31 ed xor %ebp,%ebp
2: 49 89 d1 mov %rdx,%r9
5: 5e pop %rsi
6: 48 89 e2 mov %rsp,%rdx
9: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
d: 50 push %rax
e: 54 push %rsp
f: 49 c7 c0 00 00 00 00 mov $0x0,%r8
16: 48 c7 c1 00 00 00 00 mov $0x0,%rcx
1d: 48 c7 c7 00 00 00 00 mov $0x0,%rdi
24: e8 00 00 00 00 callq 29 <_start+0x29>
29: f4 hlt
With /bin/cat as an example:
$ readelf -h /bin/cat
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x402602 <-----
Start of program headers: 64 (bytes into file)
Start of section headers: 46112 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 28
Section header string table index: 27
The memory address of the entry point is 0x402602.
402602: 31 ed xor %ebp,%ebp
402604: 49 89 d1 mov %rdx,%r9
402607: 5e pop %rsi
402608: 48 89 e2 mov %rsp,%rdx
40260b: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
40260f: 50 push %rax
402610: 54 push %rsp
402611: 49 c7 c0 60 89 40 00 mov $0x408960,%r8
402618: 48 c7 c1 f0 88 40 00 mov $0x4088f0,%rcx
40261f: 48 c7 c7 40 1a 40 00 mov $0x401a40,%rdi
402626: e8 d5 f1 ff ff callq 401800 <__libc_start_main#plt>
40262b: f4 hlt
Recommended reading:
Linux x86 Program Start Up or - How the heck do we get to main()?
What is the use of _start() in C?
Generic System V ABI

Docker Notary no trust data available

I'm new to Docker Notary and require a server to be setup for my research work. The issue with this is I am using a self-signed certificate, already overwritten the default root-ca.crt, notary-signer.crt and notary-server.crt.
Openssl validates the certificate correctly, as can be seen from the output:
subject=/C=SG/ST=Some-State/O=<value>/OU=DCT/CN=<Amazon EC2 hostname>/emailAddress=<value>
issuer=/C=SG/ST=Some-State/O=<value>/OU=DCT/CN=<Amazon EC2 hostname>/emailAddress=<value>
No client certificate CA names sent
Peer signing digest: SHA384
Server Temp Key: ECDH, P-256, 256 bits
SSL handshake has read 2348 bytes and written 431 bytes
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 4096 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES256-GCM-SHA384
Session-ID: 398FCDC32B644161D9243A25AA4E001408874E93247427609CD95E6EF8F83761
Session-ID-ctx:
Master-Key: 5427F024069D898563712EA826F2DF1582E8383F63FB13E9F6C6B6CAF1C4DC0A027942679426341F889F2E9DB0062C1D
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
TLS session ticket:
0000 - bc e4 fe 83 82 a1 1b 96-44 f7 1d 0c 9e 6f 45 8d ........D....oE.
0010 - 93 1e 5a c2 8c 9f 72 db-f6 45 4a 86 69 fe 30 20 ..Z...r..EJ.i.0
0020 - 98 9f 08 3d f5 bd ad d5-65 df 48 58 e4 6c f9 06 ...=....e.HX.l..
0030 - b6 28 e7 df 03 04 ac ad-ea 87 2c d8 db 64 73 44 .(........,..dsD
0040 - 0a b7 26 fe 2f a7 39 9c-5d 25 ca 21 68 76 37 26 ..&./.9.]%.!hv7&
0050 - 5e 0b d7 ea be 97 ea c8-16 b6 b0 04 30 13 0d 1e ^...........0...
0060 - 01 98 5e cf a1 58 61 df-30 14 d8 a6 f5 c0 7b 85 ..^..Xa.0.....{.
0070 - 11 cb 4c 73 93 e3 1e 53- ..Ls...S
Start Time: 1494736027
Timeout : 300 (sec)
Verify return code: 18 (self signed certificate)
HTTP/1.1 400 Bad Request
Content-Type: text/plain; charset=utf-8
Connection: close
400 Bad Requestclosed
I have also edited server-config.json to reflect the certificate algorithm to RSA and edited config.json in cmd/notary to point to my hostname:4443.
The issue is that after i rebuild docker notary and run the command
notary -s https://<server hostname>:4443 -d ~/.docker/trust list docker.io/library
I get this result
* fatal: no trust data available
I find it frustrating that for the last 2 days I have been pondering over this and the documentation is not very clear on how to do it either (probably because it is not even in stable yet).
Any help on this would be appreciated!

Need help reverse engineering a CRC16

I'm trying to connect to the Safecom TA-810 (badge/registration system) to automate the process of calculating how long employee's have worked each day. Currently this is done by:
Pulling the data into the official application
Printing a list of all 'registrations'
Manually entering the values from the printed lists into our HR application
This is a job that can take multiple hours which we'd like to see automated. So far the official tech support has been disappointing and refused to share any details.
Using wireshark I have been capturing the UDP transmissions and have pretty much succeeded in understanding how the protocol is built up. I'm only having issues with what i suppose is a CRC field. I don't know how it is calculated (CRC type and parameters) and using which fields ...
This is how a message header looks like:
D0 07 71 BC BE 3B 00 00
D0 07 - Message type
71 BC - This i believe is the CRC
BE 3B - Some kind of session identifier. Stays the same for every message after the initial message (initial message has '00 00' as value)
00 00 - Message number. '01 00', '02 00', '03 00'
Some examples:
Header only examples
E8 03 17 FC 00 00 00 00 -> initial request (#0, no session nr)
D0 07 71 BC BE 3B 00 00 -> Initial response (#0, device sends a session nr)
4C 04 EF BF BE 3B 06 00 -> Message #6, still using the same session # as the initial response
Larger example, which has data
0B 00 07 E1 BE 3B 01 00 7E 45 78 74 65 6E 64 46 6D 74
I've also been trying to figure this out by reading the disassembled code from the original application. The screenshot below happens before the socket.sendto and seems to be related.
Any help will be extremely appreciated.
EDIT: Made some success with debugging the application using ollydbg. The CRC appears in register (reversed) EDX at the selected line in the following screenshot.
Take a look at CRC RevEng. If you can correctly identify the data that the CRC is operating on and the location of the CRC, you should be able to determine the CRC parameters. If it is a CRC.
I've managed to create a php script that does the CRC calculation by debugging the application using OllyDbg.
The CRC is calculated by Adding up every 2 bytes (every short). if the result is larger than a short, the 'most significant short' is added to the 'least significant short' until the result fits in a short. Finally, the CRC (short) is inverted.
I'll add my php script for completeness:
<?php
function CompareHash($telegram)
{
$telegram = str_replace(" ", "", $telegram);
$telegram_crc = substr($telegram, 4, 4);
$telegram = str_replace($telegram_crc, "0000", $telegram);
echo "Telegram: ", $telegram, ', Crc: ', $telegram_crc, ' (', hexdec($telegram_crc), ')<br />';
$crc = 0;
$i = 0;
while ($i < strlen($telegram))
{
$short = substr($telegram, $i, 4);
if (strlen($short) < 4) $short = $short . '00';
$crc += hexdec($short);
$i += 4;
}
echo "Crc: ", $crc, ', inverse: ', ~$crc;
// Region "truncate CRC to Int16"
while($crc > hexdec('FFFF'))
{
$short = $crc & hexdec ('FFFF');
// Region "unsigned shift right by 16 bits"
$crc = $crc >> 16;
$crc = $crc & hexdec ('FFFF');
// End region
$crc = $short + $crc;
}
// End region
// Region "invert Int16"
$crc = ~$crc;
$crc = $crc & hexdec ('FFFF');
// End region
echo ', shifted ', $crc;
if (hexdec($telegram_crc) == $crc)
{
echo "<br />MATCH!!! <br />";
}
else
{
echo "<br />failed .... <br />";
}
}
$s1_full = "E8 03 17 FC 00 00 00 00";
$s2_full = "D0 07 71 BC BE 3B 00 00";
$s3_full = "D0 07 4E D4 E1 23 00 00";
$s4_full = "D0 07 35 32 BE 3B 07 00 7E 44 65 76 69 63 65 4E 61 6D 65 3D 54 41 38 31 30 00";
$s5_full = "0B 00 39 6C BE 3B 05 00 7E 52 46 43 61 72 64 4F 6E";
CompareHash($s1_full);
CompareHash($s2_full);
CompareHash($s3_full);
CompareHash($s4_full);
CompareHash($s5_full);
?>
Thanks for the feedback!