I'm attempting to programmatically replace an embedded image within an OFT file (An Outlook message template), which is in Compound File Binary Format (because using anything human readable would make my life too easy).
To work with this file, I'm using OpenMCDF.
Since embedded images are basically file attachments, I can get the stream for the image like so:
static string FOOTER_IMG = "__substg1.0_37010102"; //Stream ID for embedded JPEG footer image
static string ATTACHMENT2 = "__attach_version1.0_#00000001"; //Storage ID for attached footer image
// ...
CFStream imgStream2 = file.RootStorage.GetStorage(ATTACHMENT2).GetStream(FOOTER_IMG);
I can then update that stream with the bytes from my desired image like so:
byte[] img2 = File.ReadAllBytes(footerimgFile); // New file
imgStream2.SetData(img2);
However, when I load the .OFT file in Outlook, the image no longer loads and I get a red X saying the image could not be loaded. I spent hours analyzing every bit of that OFT file, and the only thing that changed between the original template and the new template is that one stream that I replaced.
Here's where things get weird:
I noticed I could replace the bytes with the same exact bytes I had before and save it, so my saving mechanism is working. I thought maybe the OFT template stores some sort of hash of the image which has to match up. So I modified a few random bytes, and the image still loads (sometimes with some funky colors). Eventually, I realized it only breaks if the new image contains fewer bytes than the original image. I can replace the image with a larger image, and that works! I can also just pad a smaller image with trailing zeros at the end of the stream, and it still works.
This led me to come up with a true hackerific masterpiece:
if (img2.Length < 5585) img2 = img2.Concat(new byte[5585 - img2.Length]).ToArray();
Basically, if img2 is too small, I pad on enough bytes to make it the same size as the original image (5585 bytes to be exact). So this works. But.. yea.
My Question:
Does the Microsoft OFT file format store the byte count for attachments in some other stream or some other CDF container? If this was a standard property of CDF, you'd think OpenMCDF would update this count. This leads me to believe this is a property of the OFT file format, which OpenMCDF would of course know nothing about.
Why would writing a smaller stream corrupt the file, where writing a larger stream work?
Update:
From what I've read so far, the __properties_version1.0 stream contains a list of pointers (offsets?) to mark where various other streams are. I'm guessing something in here needs to be updated. Currently, I have these streams in the attachment container:
From what I can tell __properties_version1.0 doesn't change hardly at all between the first attachment (a 36,463 byte file) and the second attachment (a 5,585 byte file). The __properties_version1.0 for the second attachment is:
There's only a set of 8 bytes that change between those two attachments. In attachment 1 we have:
6F 8E 00 00 03 00 2D 00
In attachment 2 (pictured above) we have:
D1 15 00 00 03 00 6F 08
Are those offsets? Doesn't seem to be a range, or the numbers would go up. Those numbers are also way too big to be file sizes. Plus, it seems redundant to store file sizes in here anyway. So, I'm once again at a loss as to why changing the size of the 0x37010102 stream causes the image to no longer load.
Another thing that makes zero sense. I can change the size of the first attachment with either larger or smaller files, and nothing breaks. However, there's absolutely no difference between any stream in those two containers except the data in the 0x37010102 stream. Why does this approach work in one attachment and not the other?
Update 2:
I have noticed the two differences in the __properties_version1.0 stream between the two attachments do correspond to the file sizes:
6F 8E 00 00 03 00 2D 00 // Attachment 1
D1 15 00 00 03 00 6F 08 // Attachment 2
6F 8E seems to be a little-Endian representation of the file size, as 8E6F in decimal would be 36463, which is the number of bytes in the first attachment. 15D1 in decimal is 5585, the size of the second attachment. So, this stream definitely is storing file sizes. Now to see if I fix those bytes if the file becomes uncorrupted.
Update 3:
So, changing those bytes fixes a previously corrupted file, so that's the key! Now just to find a way to do this programmatically.
Are you working with embedded HTML images (which are just regular image attachments) or embedded RTF images (which OLE storage)?
Do you simply truncate a particular stream without adjusting any other properties?
Well, it's times like this I feel like an uber nerd. Here's the code that fixes the problem. Note, the byte offsets in propBytes might be different if you had other properties in the attachment.
// Fix file size on prop stream
var propStream = file.RootStorage.GetStorage(ATTACHMENT2).GetStream("__properties_version1.0");
var propBytes = propStream.GetData();
propBytes[0xb0] = (byte)(img2.Length & 0xFF);
propBytes[0xb1] = (byte)(img2.Length >> 8);
propStream.SetData(propBytes);
However, I like this solution better than padding extra zeros.
I think the real solution would be to use a third party library that deals with .MSG format, however I could not find any that don't make you install Outlook or Exchange on the server (which we can't do) or that are free (we have zero budget for this).
Related
I am working on a POS application that supports EMV cards. I am able to read card data from a Verifone MX card reader in TLV, but I am facing issues in decoding the TLV data to readable data.
I am able to Split the data into TLV Tags and its values. The resultant value is in Hex instead of Decoded text.
Example:
This is a sample TLV data (I got this sample TLV Data here
6F2F840E325041592E5359532E4444463031A51DBF0C1A61184F07A0000000031010500A564953412044454249548701019000
When i check this TLV in TLVUtil, I get data in certain Tags in readable format (like Tag 50 here).
The Closest I could get in my application is this:
Tag Value
50 56495341204445424954
4F A0000000031010
61 4F07A0000000031010500A56495341204445424954870101
6F 840E325041592E5359532E4444463031A51DBF0C1A61184F07A0000000031010500A56495341204445424954870101
84 325041592E5359532E4444463031
87 1
90
A5 BF0C1A61184F07A0000000031010500A56495341204445424954870101
BF0C 61184F07A0000000031010500A56495341204445424954870101
I would like to know if there is any way to identify certain tags that need to be converted from Hex to string or if there is any TLV Parser and decoder available in .Net that can replicate the TLVUtil tool.
Complete list of EMV tags and are available in EMVCo 4.3 specification book 3 -
you can download from here - https://www.emvco.com/download_agreement.aspx?id=654
How data is represented differs from field to field. Check 'Annex A - Data Elements Dictionary'
Details on encoding is mentioned in section 4.3
Read both the sections and your problem solved.
There are only a few tags that need to be converted to string. Generally tags that are put on POS screen personalized in hex equivalent of readable string.
5F20 : Cardholder Name
50 : Application Label.
5F2D : Language Preference
You must know which tags can be converted.
As it seems to me, programmatically you can identify something like,
Tag is of one byte ( 5A - Pan number ) or it contain 2 byte ( 5F20 - CARD HOLDER NAME), AND
length is of 1 byte or 2 byte AND
Tag is primitiv or constructed. More you can read Here
and if you know the list you can get something useful Here, It define the format of tag that you are looking for.
Here you can hard coded the format as it is well defined.
Hope it helps.
That data beginni g with 6F is a File Control Information (FCI) responded by an EMV card after SELECT command. There is an example in this video also decoded and explained.
https://youtu.be/iWg8EBhsfjY
Its easy check it out
Wee bit of background to set the scene : we've told a client he can provide us with images of any type and we'll put them on his reports. I've just had a quick run through of doing this and found that the reports (and other things between me and them) are all designed to only use SVGs.
I thought I'd struck gold when I found online that you can convert an image from a jpg or PNG into an SVG using free tools but alas I've not yet succeeded in getting an SVG stored as bytes in the DB in a format that allows me to use it again after reading it back out.
Here's a quick timeline of what followed leading up to my problem.
I use an online tool to generate an SVG from a PNG (e.g., MobileFish)
I can open and view it in Firefox and it looks ok
I ultimately need the SVG to be stored as bytes in the DB, from where the report will pull it via a webpage that serves it up as SVG. So I write it as bytes into a SQL data script. The code I use to write these bytes is included below.
I visit said webpage and I get an error that there is an "XML parsing error on Line 1 Column 1" and it shows some of my bytes. They begin "3C73"
I revisit the DB and compare the bytes I've just written there with some pre-existing (and working ones). While my new ones begin "3C73", the others begin "0xFFFE".
I think I've probably just pointed out something really fundamental but it hasn't clicked.
Can someone tell me what I've done that means my bytes aren't stored in the correct encoding/format?
When I open my new SVG in Notepad++ I can see the content includes the following which could be relevant :
<image width="900" height="401" xLink:href="data:image/png;base64,
(base 64 encoded data follows for 600+ lines)
Here's the brains of the code that turns my SVG into the bytes to be stored in DB :
var bytes = File.ReadAllBytes(file);
using (var fs = new StreamWriter(file + ".txt"))
{
foreach (var item in bytes)
{
fs.Write(String.Format("{0:X2}",item));
}
}
Any help appreciated.
Cheers
Two things:
SVGs are vector images, not bitmap files. All that online tool is doing is taking a JPEG and creating a SVG file with a JPEG embedded in it. You aren't really getting the benefit of a true SVG image. If you realise and understand that, then no worries.
SVG files are just text. In theory there is no reason you can't just store them as strings in your db. As long as the column is big enough. However normally if you are storing unstructured files in a db, the preferred column type to use is a "Blob".
http://technet.microsoft.com/en-us/library/bb895234.aspx
Converting your SVG file to hex is just making things slower and doubling the size of your files. Also when you convert back, you have to be very careful about the string encoding you are using. Which, in fact, sounds like the problem you are having.
I am suspecting you are doing it incorrectly. SVG is simply and XML based vector image format. I guess your application might be using SVG image element and you need to convert your png image to base64 encoded string .
I am intercepting the paste event for a richtextbox, in order to process the contents before pasting. If it contains tables or images etc. I need to do some custom stuff. If the copied selection is from Word 2010 and consists of mixed content (eg. text and table/image) Word places the content on the clipboard in a number of formats. These includes HTML and RTF, but I would rather work with WordML. I've used ClipSpy to check what formats and data is actually put on the clipboard and the "Embed source" format seems to be the format containing WordML. I would think this could be opened as a Package:
var stream = Clipboard.GetData("Embed Source") as MemoryStream;
var package = Package.Open(stream);
It throws an EndOfStreamException and I'm thinking it migth be wrapped in something else. I can write the stream to disk and open it using 7-zip and see that the contents are as expected.
So basically two questions:
Is "Embed source" the right DataObject to get the WordML?
If it is, how do I deserialize it?
After saving the stream to disk and doing a binary comparison to a proper docx, I figured out that it was in fact wrapped in a Compound Document File: http://www.openoffice.org/sc/compdocfileformat.pdf. I googled the first few bytes
D0 CF 11 E0 A1 B1 1A E1
which is the identifier of the CDF format.
The package can be extracted from the Compound file using OpenMCDF.
A bit of background information:
Inorder to read/write to SLE4442 memory cards, my app is currently using an Omnikey Cardman 3021 USB card reader, a Sumbsembly Smartcard API (external dll) which is capable of wrapping CT-API calls (directed to omnikey's dll) so that I can read/write the memory card in my c# app.
The only problem here is that Omnikey only provides a 32-bit dll of their CT-API. I asked if they are going to produce a 64-bit version, but they couldn't be bothered.
Current situation:
Inorder to make my application 64-bit capable, I must rewrite it using Windows WinSCard API. The problem here is that there are no specific examples on the web how to do it. Also getting hold of working APDU commands is nearly impossible, but I've managed to aquire two slightly different versions that sort of work.
I have googled this a hundred times over many months and with what I have managed to gobble together I can finally read the SLE4442 memory card. But for the life of me I can't get writing to work.
The code:
I'm not going to post the entire code into this first post (if need be I can do it later or provide a link to the source code).
But I'll outline the basic steps.
1) SCardEstablishContext
2) Get the reader name via SCardListReaders
3) SCardConnect
4) Read entire memory with SCardTransmit and APDU new byte[] { 0xFF, 0xB0, 0, 0, 0 };
5) Verify pin with SCardTransmit and APDU new byte[] { 0xFF, 0x20, 0, 0, 3, 0xFF, 0xFF, 0xFF }; (Note that this does return 0x90;0x00 as a response, which means the verification should have been succesful)
6) Try to write with ScardTransmit and APDU new byte[] { 0xFF, 0xD6, 0, 0, 50, 1 }; (try to write value 1 at memory position 50) - I have also tried using an APDU with the first parameter being 0x00 and/or the second byte being 0xD0. The response has never been 0x90;0x00 so I assume there is an error during writing, but I haven't been able to find any meaning to the error codes returned.
Possible causes:
Because I can read a memory card with the WinSCard API then it must be possible to also write to one (side note - the memory card(s) that I try to write to are in in working condition, I haven't locked them down by failing to verify the PIN 3 times).
1) Maybe the write APDU command is wrong. Could be that the instruction byte (second byte) is incorrect, or the memory location uses some sort of an extended coding scheme.
2) Maybe the verify command didn't actually verify. As in the command itself is fine, which is why 0x90 was returned, but I must call or setup something first.
3) Just a hunch, but I think that this is the real culprit. While googling I did find some vague references to having to call the SCardControl method with parameter IOCTL_SMARTCARD_SET_CARD_TYPE and setting the card type to SLE4442. But again no working examples anywhere and my trial-and-error testing resulted in failures. I got "One or more of the supplied parameters could not be properly interpreted." and some other error messages as well, can't remember what they all were. Assuming the code I copy-pasted from google code has the right descriptions for the error codes.
What I need:
What I need is someone to post or direct me to a site that has full+working code in c# for read/write SLE4442 using WinSCard API and it must work in both 32-bit and 64-bit enviroments.
The code doesn't have to be foolproof - eg. handling every possible error situation nicely. I should be able to do that myself. But if it is (including the APDU command result descriptions - eg. 0x90;0x00 is success, but 0x6B;0x4D is... etc...) then all the better.
APDU for writing to card, in your example, should be:
FF D6 00 50 01 01
Our Omnikey (3121) terminal writes data on SLE4442/SLE4442 cards perfectly, try this APDU:
FF D6 00 04 10 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E AA
see also:
http://acs.com.hk/drivers/eng/API_ACR122U.pdf, chapter 5.4
FF,D2,00,01,03,01,02,03 (new pin = 1,2,3) does not work (also after verifying existing PIN:
6D,00,FF,20,00,00 03,FF,FF,FF) - It returns error code 6D00.
For us (yet) not a huge problem, as we code our cards now with the Xiring XiMax terminal (programmable terminal with it's own SDK, the terminal can store data in flash memory).
However, we need to find a solution soon. I am interested in your (future) findings.
Do you have the Omnikey SDK? We can send you working examples in C++ that can change PSC (pin) and data on SLE4442/5542 cards.
This week (so not tomorrow, as previously stated, we have many things going on and are constantly ) we will test the (so far) working code with the Omnikey 1021). Hope to help you out.
We always have 50+ Omnikey 3121 readers on stock and we can offer you these (1+) for a far batter price. Let me know if you are interested.
I am also interested what where you are located and what applications you are developing.
I have simple database in Access .mdb file, but I don't know how to deal with: "parameter not valid" exception when Im creating Image from stream.
I'v read that I need to strip 78 bytes offset (from here) but I still get a "parameter not valid" error
when I call FromStream, even after stripping off the first 78 bytes.
This doesn't work for me:
byte[] abytPic = (byte[])dt.Rows[0]["Photo"]; byte arrary with image
if ((abytPic[0] == 21) && (abytPic[1] == 28)) //It's true
{
byte[] abytStripped = new byte[abytPic.Length - 78];
System.Buffer.BlockCopy(abytPic, 78, abytStripped, 0, abytPic.Length - 78);
msPic = new emoryStream(abytStripped);
}
If you are reading the data directly from MS Access, you do not need to strip any header information.
Assuming the image is stored as a BLOB, which is the most common, here is code to read in the array of bytes from the database and store as an image file (sorry, VB instead of C#):
Dim varBytes() As Byte
Using cn As New OleDbConnection(myConnectionString)
cn.Open()
sqlText = "SELECT [myColumn] " _
& "FROM [myTable] " _
& "WHERE ([mySearchCriteria] = '" & mySearchTerm & "')"
Using cm As New OleDbCommand(sqlText, cn)
Dim rdr As OleDbDataReader
rdr = cm.ExecuteReader
rdr.Read()
varBytes = rdr.GetValue(0)
End Using
End Using
My.Computer.FileSystem.WriteAllBytes(myPath & "\myFile.emf", varBytes, True)
This example that I had laying around is one where I knew the files in the database were .emf images. If you know the extension, you can put it on the file name. If you don't, you can leave it blank and then open the resulting with an image viewer; it should start. If you need to find the extension or file type, once it is saved as a file, you can open it with any hex editor and the file type will be available from the header information.
Your question is a little bit unclear, so I'm not sure the above code is exactly what you want, but it should get you a lot closer.
EDIT:
This is the VB code that takes the array of bytes, loads it into a MemoryStream object, and then creates an Image object from the Stream. This bit of code worked just fine, and displayed the image in a picturebox on my form.
Dim img As Image
Dim str As New MemoryStream(varBytes)
img = Image.FromStream(str)
PictureBox1.Image = img
If the C# equivalent of this is not working for you, then the problem is likely in how the image is stored in the MS Access database.
EDIT:
If the image in your database is stored as a 'Package' and not a 'Long binary data', then you will need to strip the header information that MS Access adds. I've been playing with the 'Package' type of image storage with a simple .jpg file. The header in this case is much longer than 78 bytes. In this instance, it's actually 234 bytes, and MS Access also added some information to the end of the original file; about 292 bytes in this case.
It looks like your original approach was correct, you will just need to determine how many bytes to strip off the front and rear of the Byte array for your situation.
I determined it for my file by comparing the original image file, and the file exported from the database, (not to a Stream object, see my first code) in a hex editor. Once I figured out how much information (header and footer) was added by MS Access, I then knew how many bytes needed to be stripped.
EDIT:
The size of the header added by MS Access when the image is stored as 'Package' varies depending on the file type, and the original location (full path information) of the image when it was dumped into the MS Access database. So, even for the same file type, you may have a different number of bytes to strip from the header for each file.
This makes it a lot more difficult, because then you will have to scan the byte array until you find the normal start-of-file information for that file type, and then strip everything before it.
All this headache is one of the reasons that it is better to store images as BLOBs 'Long binary data' in a database. Retrieval is much easier. I don't know if you have the option to do this, but if so, it would be a good idea.
I do not believe that your problem is with the database. "Parameter not valid" exceptions when dealing with imaging can be a total pain as I have dealt with them before. They're not very clear on what the problem is.
How exactly was the image placed into the database? There could be a problem with writing the image into the database before you've even attempted to pull it. Also, what file type is the image?
EDIT Here is some sample code that I've used before to get an image from a byte array.
//takes an array of bytes and converts them to an image.
private Image getImageFromBytes(byte[] myByteArray)
{
System.IO.MemoryStream newImageStream = new System.IO.MemoryStream(myByteArray, 0, myByteArray.Length);
return Image.FromStream(newImageStream, true);
}