“Why is it possible for everyone, but I can’t?” Or reverse the API and get data from eToken / Sudo Null IT News

“Why is it possible for everyone, but I can’t?” Or reverse the API and get data from eToken / Sudo Null IT News Электронная цифровая подпись

Sudo null – latest it news

Once at our enterprise the task was to increase the level of security when transferring very important files . In general, word for word, and we came to the conclusion that we need to transfer using scp, and store the private key of the certificate for authorization on an eToken type remote control, since we have accumulated a certain amount of them.

The idea seemed good, but how to implement it? Then I remembered how once the client bank did not work in accounting, swearing at the lack of a library with the speaking name etsdk.dll, I was curious and I got into picking it.

In general, the development company distributes the SDK on its website, but for this it is necessary to register as a software development company, and this is clearly not me. I could not find documentation on the Internet, but curiosity prevailed and I decided to figure it out myself. The library – here it is, there is time, who will stop me?

The first thing I did was launch the NirSoft DLL Export Viewer, which showed me a decent list of functions exported by the library. The list looks good, the logic and sequence of actions when working with tokens are traced. However, one list is not enough, you need to understand what parameters, in what order to transfer and how to get the results.

And then it was time to remember our youth and launch OllyDbg version 2.01, load the ccom.dll cryptosystem Crypto-Com library used by the client bank and using the etsdk.dll library into it, and begin to understand how they do it.

Since there is no executable file, the library will be loaded using the loaddll.exe from the Olly bundle, so we might not even dream of full debugging. In fact, we will use the debugger as a disassembler (yes, there is an IDA, but I never worked with it and in general it is paid).

We call the context menu and select Search for> All intermodular calls, arrange the result by name and look for functions starting with ET * and do not find it. This means that the library is connected dynamically, so in the same list we look for GetProcAddress calls, look at them and, with a certain attempt, we come across an attempt to find out the address of the ETReadersEnumOpen function, and after looking a little further, we see the memory addresses loading of all functions from the etsdk.dll library.

Not bad. The received function addresses are stored in memory by commands like MOV DWORD PTR DS: [10062870], EAX, select each such command, call the context menu and select Find references to> Address constant. In the window that opens, the current command and all places of the function call will be shown. Let’s go through them and put down a comment with the name of the function being called – this will make our future life easier.

It’s time to figure out how to properly call these functions. Let’s start from the beginning and learn how to get information about readers. We pass to the place of calling the ETReadersEnumOpen function and, thanks to the comments left, we see that ETReadersEnumOpen, both ETReadersEnumNext and ETReadersEnumClose are concentrated in one function – obviously, it is, among other things, engaged in obtaining a list of readers.

All functions use the cdecl calling convention. This means that the result will be returned in the EAX register, and the parameters will be passed through the stack from right to left. In addition, this means that all parameters have the dimension of a double word, and if they do not, they expand to it, which will simplify our lives.

Let’s see the neighborhood of the ETReadersEnumOpen call:

One parameter is passed, which is a pointer to some local variable, and after the call, if the result is not 0, control is transferred to some explicit debugging code, and if it is equal, we go ahead (the JGE command transfers control if the ZF and OF flags are equal, and the flag OF command TEST always resets to 0). Thus, I conclude the following order: a variable is passed to the function by reference, into which some enumeration identifier returns, and as a result, the function returns an error code or 0 if there is no error.

Go to ETReadersEnumNext:

Two parameters are passed to it: the value of the variable obtained using ETReadersEnumOpen (the identifier of the enumeration) and a pointer to a local variable, where, obviously, the next value is returned. Moreover, since the parameters are transferred in order from right to left, it is the first parameter that is the identifier, and the second is the result pointer. The error code is still returned via EAX, and judging by the design of the loop, it is used not only to report an error, but also to indicate that there is nothing more to list.

ETReadersEnumClose is even simpler: an enumeration identifier is passed to it, but the result does not bother anyone.

It’s time to test our understanding of these features. Here I am forced to make a small digression: the fact is that I am a system administrator by profession, and therefore serious compiled programming languages ​​are not entirely mine. For work, I need Bash and Python for Linux more, but if I need to quickly pile something under Windows, I use AutoIt , which I liked .

The pluses for me are:

  • mobility (the interpreter and script editor are fully portable),
  • simple work with GUI,
  • the ability, if not enough functionality, to connect external libraries (I know what is trivial for a programming language, but not so trivial for a scripting language),
  • the ability to build scripts into executable files.

Minuses:

  • Implicit type conversion and insufficient number of types represented.
  • Lack of records (as well as associative arrays) and OOP (in general, it exists, but only for COM objects, so it wouldn’t be there).

This digression was to the fact that we will sculpt examples of the use of functions on AutoIt. Calling functions from external libraries, in connection with implicit typing in the language, looks somewhat clumsy, but it works.

Let’s start: frankly, we have no idea what and what size the functions return, so we will give a large buffer to start with, and see what happens. Code to start:

Dim $ETSdkDll=DllOpen(Dim $buf=DllStructCreate(
Func PrintBuf($buf)
	For $i=1To DllStructGetSize($buf)
		ConsoleWrite(Hex(DllStructGetData($buf,Next
	ConsoleWrite(@CRLF)
EndFunc
ConsoleWrite(
PrintBuf($buf)
$result=DllCall($ETSdkDll,
)
ConsoleWrite(
PrintBuf($buf)
ConsoleWrite(

Having executed it, we get a conclusion like this:

Buffer before: 0000000000000000000000000000000000000000000000000000000000000000 
Buffer after:  446F C8 0100000000000000000000000000000000000000000000000000000000Returnvalue: 0

We run it several times and see that only the first 4 bytes are changed, which means that a 4-byte integer is used as an identifier, which means we can comb the code to call this function a bit to this state:

Func ETReaderEnumOpen()
	Local $id=DllStructCreate(
	Local $result=DllCall($ETSdkDll,
	)
	Return $result[0]?0:DllStructGetData($id,1)
EndFunc

Similar experiments with the ETReadersEnumNext function showed the following: the first 260 bytes of the buffer contain the reader name and zeros. A sequential call to this function was listed to me by all the readers in the system (for example, three were created in advance under ruToken). Readers for eToken are created dynamically, depending on the number of connected tokens and, most interestingly, they have the 261st byte of the buffer set to one, which, apparently, indicates the reader’s compatibility with our library. If you look at the disassembled code, you can see that records with the 261st byte equal to 0 are not processed. All other bytes to the end of the kilobyte buffer for all readers are 0 and do not differ.

So, we figured out the readers, now we need to understand what’s next. After examining the list of functions, I came to the conclusion that the call sequence should be as follows: first we bind the desired reader, at this stage we can find out general information about the inserted token, then we make a login, and after that we get access to the file system. Thus, the next in line functions are ETTokenBind and ETTokenUnbind.

ETTokenBind looks complicated and incomprehensible, but after digging around for a while, I came to the conclusion that two parameters are passed to the function, the first of which is a pointer to a buffer of 328 bytes (0x0148), and the second is a pointer to a string with the name of the reader. Through experiments, it was found that the identifier is returned in the first four bytes of the buffer (hereinafter: the binding identifier). Why is the rest of the buffer allocated – for now, a mystery. What tokens I would not experiment with, the remaining 324 bytes of the buffer remained filled with zeros. The specified identifier, which is logical, is successfully used as an argument to the ETTokenUnbind and ETTokenRebind functions.

The next function in line is ETRootDirOpen. It takes three parameters: a pointer to the result, a binding identifier, and a constant. The function has several features.

First: the returned result of this function is checked not only for equality to zero (success), but also for the equality of the lower two bytes to the number 0x6982, and if the result is equal to this number, control is transferred to the function, which subsequently calls ETTokenLogin, and then tries again call ETRootDirOpen. From this we can conclude that 0x6982 is an error code meaning “Authorization Required”. Looking ahead, I’ll say that all other functions that work with files and folders are arranged in the same way.

Second: as one of the parameters, this function takes the constant 0xF007. There are no calls with other constants in the code. Perhaps this constant somehow characterizes the information written to the token (a lot of root folders?). I tried to go through brute force on all values ​​of a two-byte constant and the token responded only to the values ​​0x0001, 0xF001-0xF00B (by the way, I never asked for authorization). Later, I found out that the same folders are available on a freshly initialized token. After thinking about this for a while, I came to the conclusion that according to the developer’s plan, different root folders are used for different purposes, and somewhere it is written that 0xF007 is for keys.

Third: the value returned by the function is not visible in the screenshot, but returns to the middle of the 328-byte buffer that was allocated earlier, from which we can conclude that that buffer is a structure that stores a variety of identifiers and data related to the token under consideration.

Since the authorization attempt has gone, it’s time to deal with it. The ETTokenLogin function receives two parameters: a binding identifier and a pointer to a buffer. At first I thought that the buffer was used to output some kind of result, but experiments showed that the following algorithm was used: if the pointer is zero or points to an empty string, the library draws an interface window asking for a password, if it points to a non-empty string – this string used as a password. ETTokenLogout accepts only one parameter: the binding identifier.

Читайте также:  Электронная подпись (ЭЦП) для суда

The following group of functions: ETDirEnumOpen, ETDirEnumNext, and ETDirEnumClose. You can try to unravel them without looking into the code. In general, they should work just like ETReadersEnum *, with the only difference being that the identifier of the current folder will be passed as a parameter to ETDirEnumOpen. Check – it works.

The ETFilesEnumOpen, ETFilesEnumNext, and ETFilesEnumClose function groups are simply required to work in the same way, but we cannot verify this with certainty, as long as Apparently, there are no files in the root folder of the token under investigation, which means that it is time to go deeper into the folder tree using the ETDirOpen function.

This API seems to have drawn a tradition according to which the first parameter is used to return the result, so suppose this is true this time too. The second parameter, before being passed to the function, undergoes modifications using the MOVZX EDI, DI command, i.e. the word expands to a double word. Obviously, this is necessary in order to pass the double-byte folder name in the four-byte parameter. Well, the third parameter according to the logic of things should be the identifier of the open folder. We try – it turned out. ETDirClose can guess without surprises: 1 parameter – folder identifier.

So, we have learned enough to list all the files and folders on the token. The following simple code will do just that (I don’t do a call to DllCall here – it will be for all functions in the module text at the end of the article):

Func PrintDir($Id,$Prefix)
	Local $EnumId=ETDirEnumOpen($Id)
	While1
		Local $dir=ETDirEnumNext($EnumId)
		If @errorThen ExitLoop
		ConsoleWrite($Prefix&
		Local $DirId=ETDirOpen($dir,$Id)
		PrintDir($DirId,$Prefix&@TAB)
		ETDirClose($DirId)
	WEnd
	ETDirEnumClose($EnumId)
	$EnumId=ETFilesEnumOpen($Id)
	While1
		Local $file=ETFilesEnumNext($EnumId)
		If @errorThen ExitLoop
		ConsoleWrite($Prefix&WEnd
	ETFilesEnumClose($EnumId)
EndFunc
Local $EnumId=ETReaderEnumOpen()
If $EnumId ThenWhile1
		Local $reader=ETReaderEnumNext($EnumId)
		If @errorThen ExitLoop
		IfNot $reader[1] Then ContinueLoop
		Local $BindId=ETTokenBind($reader[0])
		ConsoleWrite($reader[0]&
		ETTokenLogin($BindId,
		Local $DirId=ETRootDirOpen($BindId)
		PrintDir($DirId,@TAB)
		ETDirClose($DirId)
	WEnd
EndIf
ETReaderEnumClose($EnumId)

Result in the console:

Aladdin Token JC 0:
	(dir)1921
		(dir)DDDD
			(file)0002
			(file)0003
			(file)0004
			(file)0001
		(file)A001
		(file)B001
		(file)C001
		(file)AAAA
		(file)D001

Fine!

Well, we learned how to open and view folders, it’s time to learn how to open and read files. ETFileOpen accepts 3 parameters, so first we try to do the same as for ETDirOpen: the result, file name, folder ID and break off: the developers reversed the last two parameters. Well, at least ETFileClose works without surprises.

ETFileRead. The worst function of all, because perceives as many as 5 parameters. Where so much? Let’s try to list what we need: where to read from (file), where to read (buffer), how much to read and where to read from. Let’s try to figure out what and how:

As you can see, the third parameter passed to the ETFileRead function is always 0xFFFF, so I am inclined to assume that this is the length of the read piece of data. The remaining 4 parameters come to the function that I called FileReadHere from the outside in the same order. The following figure shows the neighborhood call this function. The value of the first parameter is taken from memory at the address ESI 8. A pointer to this address is used in the FileOpenHere function (named after the same principle) and the identifier of the open file is written there. The second parameter is equal to zero, therefore, we assign it to be responsible for the start point of reading the file. The third parameter (the fourth for ETFileRead) is somehow muddy, so we will assign it as a pointer to the result buffer. The fifth parameter is unusual at all. A word from the address ESI 12 is placed in it, expanding to a double word – this is unusual, because so far all the offsets which I saw were multiples of 4 (12 is not a multiple of 4, because it is 0x12, i.e. 18 in decimal). The address ESI 10 is not mentioned anywhere in the vicinity, but ESI 0C is passed to FileGetInfoHere, so you will first have to deal with the ETFileGetInfo function. It is simple, the first parameter is the file identifier, the second is a pointer to the result buffer. After a call, 1, 2, 3, 7, and 8 bytes change in the buffer. Looking ahead, I’ll say that it turns out that the last two bytes are the file size. It is this value that is passed to the ETFileRead function and to the function that initializes the output buffer for it. The first two bytes of the ETFileGetInfo result turned out to be the file name. I did not understand the value of the third, but it was set to 1 for only one file on the token. Thus, the following order of parameters looms: file identifier, reading start point,

Since we touched on ETFileGetInfo, we should immediately implement ETDirGetInfo: the order of parameters is the same, only the identifier of the folder, not the file, is involved. Returned result: folder name by identifier.

On this, we finished reading from the token, it’s time to write on the token. Let’s start by creating a folder. Parameters of the ETDirCreate function: a pointer to the result (obviously, after creating the folder, it opens and the identifier returns here), the folder name, the identifier of the parent folder and 0. The fourth parameter is hardcoded in the code and I still do not understand what it affects. Folders are successfully created for any value. ETDirDelete accepts only 1 parameter, so this is obviously the identifier of the open folder. ETFileCreate takes five parameters: a pointer to the result, similar to ETDirCreate, folder identifier, file name, file size, and the fifth parameter. If the fifth parameter is set to a non-zero value, then the next time ETFileGetInfo is called for this file, the third byte of the result (the same incomprehensible one) will be set to 1. Thinking, I conducted an experiment and made sure that when the attribute is set, you must enter a password to access the file, if not, then this is not necessary. It’s funny that on the token with which I experimented, there was only one such file. Hope all other files are encrypted on the key of this. ETFileDelete works without surprises, similar to ETDirDelete.

The last function referenced in this library is ETFileWrite. It takes 4 arguments: file identifier, zero (the experiment shows that it is a mixture relative to the beginning of the file), a pointer to a buffer with data and data size. It is important to remember that the file does not expand. If the sum of the offset and the file length exceeds the file size, recording does not occur, so if the file size needs to be changed, the file will have to be deleted and recreated with a new size.

Further: if you recall the export table of the library, then there are 5 more functions in it, however, their call is not implemented in this library working with the Crypto-Com cryptographic information protection tool. Fortunately for us, the same bank also distributes the Message-Pro library for working with CIPF – mespro2.dll, which can also work with tokens and has a bit more in it, namely, ETTokenLabelGet call.

The screenshot shows that there are two function calls that differ in that in the first case the second parameter is zero, and in the second it is some kind of number. The third parameter is always a pointer, so suppose this is the result, and the first – it would be logical to assume that the identifier of the bundle with the token. We try to start with zero as the second parameter – the first 4 bytes in the buffer have changed to the value 0x0000000A, i.e. 10, and this is just the length of the name “TestToken” with a zero byte at the end. But if a double word is returned to the third parameter by the pointer, it turns out that the pointer to the buffer of the required size must be transferred to the second parameter. Therefore, we conclude this order: the first time we run the function so that the second parameter is a null pointer, and the third is a double word pointer. Then we initialize the buffer of the required size and run the function a second time,

But the call to 4 more functions was not implemented here, so I received their implementation with brute force and intuition: I found that if there are too few parameters to pass to the called function, it causes a critical error during program execution, this allows you to experimentally select the number of parameters of the remaining functions:

ETTokenIDGet: 3
ETTokenMaxPinGet: 2
ETTokenMinPinGet: 2
ETTokenPinChange: 2

ETTokenIDGet takes too many parameters for the return of a simple value, so run it in the same way as ETTokenGetLabel – turns on the first attempt and returns a string with numbers Written on the side of the token.

ETTokenMaxPinGet and ETTokenMinPinGet, on the contrary, have a number of parameters, ideal for returning a single numeric value. We try the first parameter – the identifier of the bundle, the second – a pointer to a number. As a result, we get the maximum and minimum possible password lengths specified in the token settings.

ETTokenPinChange, based on the name, serves to change the password to a token, respectively, should only accept the bundle identifier and a pointer to a string with a new password. We are trying for the first time, we get error code 0x6982, which, as we know, means the need to login to the token. Is logical. Repeat with login and short password – we get error 0x6416. We conclude that the password length does not comply with the policy. Repeat with a long password – fulfills.

Now we bring all the functions into one module and save it – we will include it in other projects. The text of the module is as follows:

etsdk.au3

;Func ETReadersEnumOpen()
;Func ETReadersEnumNext($EnumId)
;Func ETReadersEnumClose($EnumId)
;Func ETTokenBind($ReaderName)
;Func ETTokenRebind($BindId)
;Func ETTokenUnbind($BindId)
;Func ETTokenLogin($BindId,$Pin=
;Func ETTokenPinChange($BindId,$Pin)
;Func ETTokenLogout($BindId)
;Func ETRootDirOpen($BindId,$Dir=0xF007)
;Func ETDirOpen($Dir,$DirId)
;Func ETDirCreate($Dir,$DirId)
;Func ETDirGetInfo($DirId)
;Func ETDirClose($DirId)
;Func ETDirDelete($DirId)
;Func ETDirEnumOpen($DirId)
;Func ETDirEnumNext($EnumId)
;Func ETDirEnumClose($EnumId)
;Func ETFileOpen($File,$DirId)
;Func ETFileCreate($File,$DirId,$Size,$Private=0)
;Func ETFileGetInfo($FileId)
;Func ETFileRead($FileId)
;Func ETFileWrite($FileId,$Data,$Pos=0)
;Func ETFileClose($FileId)
;Func ETFileDelete($FileId)
;Func ETFilesEnumOpen($DirId)
;Func ETFilesEnumNext($EnumId)
;Func ETFilesEnumClose($EnumId)
;Func ETTokenLabelGet($BindId)
;Func ETTokenIDGet($BindId)
;Func ETTokenMaxPinGet($BindId)
;Func ETTokenMinPinGet($BindId)
Const $ET_READER_NAME=0Const $ET_READER_ETOKEN=1Const $ET_FILEINFO_NAME=0Const $ET_FILEINFO_PRIVATE=1Const $ET_FILEINFO_SIZE=2Dim $ETSdkDll=DllOpen(
Func ETReadersEnumOpen()
	Local $Out=DllStructCreate(
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc
Func ETReadersEnumNext($EnumId)
	Local $Reader=DllStructCreate(
	Local $CallRes=DllCall($ETSdkDll,
	)
	Local $Result[2]=[	DllStructGetData($reader,
						DllStructGetData($reader,
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:$Result
EndFunc
Func ETReadersEnumClose($EnumId)
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc
Func ETTokenBind($ReaderName)
	Local $In=DllStructCreate(
	Local $Out=DllStructCreate(
	DllStructSetData($In,1,$ReaderName)
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc
Func ETTokenRebind($BindId)
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc
Func ETTokenUnbind($BindId)
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc
Func ETTokenLogin($BindId,$Pin=
	Local $In=DllStructCreate(
	DllStructSetData($In,1,$Pin)
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc
Func ETTokenPinChange($BindId,$Pin)
	Local $In=DllStructCreate(
	DllStructSetData($In,1,$Pin)
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc
Func ETTokenLogout($BindId)
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc
Func ETRootDirOpen($BindId,$Dir=0xF007)
	Local $Out=DllStructCreate(
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc
Func ETDirOpen($Dir,$DirId)
	Local $Out=DllStructCreate(
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc
Func ETDirCreate($Dir,$DirId)
	Local $Out=DllStructCreate(
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc
Func ETDirGetInfo($DirId)
	Local $Out=DllStructCreate(
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc
Func ETDirClose($DirId)
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc
Func ETDirDelete($DirId)
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc
Func ETDirEnumOpen($DirId)
	Local $Out=DllStructCreate(
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc
Func ETDirEnumNext($EnumId)
	Local $Out=DllStructCreate(
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc
Func ETDirEnumClose($EnumId)
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc
Func ETFileOpen($File,$DirId)
	Local $Out=DllStructCreate(
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc
Func ETFileCreate($File,$DirId,$Size,$Private=0)
	Local $Out=DllStructCreate(
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc
Func ETFileGetInfo($FileId)
	Local $Out=DllStructCreate(
	Local $CallRes=DllCall($ETSdkDll,
	)
	Local $Result[3]=[	DllStructGetData($Out,
						DllStructGetData($Out,
						DllStructGetData($Out,
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:$Result
EndFunc
Func ETFileRead($FileId)
	Local $FileInfo=ETFileGetInfo($FileId)
	If @errorThen Return SetError(@error,0,False)
	Local $Out=DllStructCreate(
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc
Func ETFileWrite($FileId,$Data,$Pos=0)
	$Data=Binary($Data)
	Local $DataSize=BinaryLen($Data)
	Local $In=DllStructCreate(
	DllStructSetData($In,1,$Data)
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc
Func ETFileClose($FileId)
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc
Func ETFileDelete($FileId)
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc
Func ETFilesEnumOpen($DirId)
	Local $Out=DllStructCreate(
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc
Func ETFilesEnumNext($EnumId)
	Local $Out=DllStructCreate(
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc
Func ETFilesEnumClose($EnumId)
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc
Func ETTokenLabelGet($BindId)
	Local $Out1=DllStructCreate(
	Local $CallRes=DllCall($ETSdkDll,
	)
	If $CallRes[0] Then Return SetError($CallRes[0],0,False)
	Local $Out2=DllStructCreate(
	$CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out2,1)
EndFunc
Func ETTokenIDGet($BindId)
	Local $Out1=DllStructCreate(
	Local $CallRes=DllCall($ETSdkDll,
	)
	If $CallRes[0] Then Return SetError($CallRes[0],0,False)
	Local $Out2=DllStructCreate(
	$CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out2,1)
EndFunc
Func ETTokenMaxPinGet($BindId)
	Local $Out=DllStructCreate(
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc
Func ETTokenMinPinGet($BindId)
	Local $Out=DllStructCreate(
	Local $CallRes=DllCall($ETSdkDll,
	)
	Return $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Читайте также:  В случае продления сертификата ключа проверки электронной подписи и истечения срока действия сертификата подписи, какие шаги необходимо предпринять для обновления электронной подписи?

So, we can do whatever we want with the token file system. To demonstrate this, I wrote a simple script that will copy the contents from one token to another. Script of the Proof-of-concept level, i.e. there will not be a lot of checks that should have been in the “correct” application, but it will allow us to get a second valid token.

eTokenCopy.au3
#include <etsdk.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#NoTrayIcon
Opt(
Opt(
Opt(Const $Title=Const $GUISize[2]=[250,100]
Dim $SrcCtrl,$DstCtrl,$ListTimer
Func TokenCopyDir($SrcId,$DstId)
	Local $Name,$SrcSubId,$DstSubId,$SrcInfo,$SrcData
	; Проход по папкам с рекурсией
	Local $EnumId=ETDirEnumOpen($SrcId)
	While1
		$Name=ETDirEnumNext($EnumId)
		If @errorThen ExitLoop
		$SrcSubId=ETDirOpen($Name,$SrcId)
		$DstSubId=ETDirOpen($Name,$DstId)
		If @errorThen
			$DstSubId=ETDirCreate($Name,$DstId)
		EndIf
		TokenCopyDir($SrcSubId,$DstSubId)
		ETDirClose($SrcSubId)
		ETDirClose($DstSubId)
	WEnd
	ETDirEnumClose($EnumId)
	; Проход по файлам
	$EnumId=ETFilesEnumOpen($SrcId)
	While1
		$Name=ETFilesEnumNext($EnumId)
		If @errorThen ExitLoop
		$SrcSubId=ETFileOpen($Name,$SrcId)
		$SrcInfo=ETFileGetInfo($SrcSubId)
		$DstSubId=ETFileOpen($Name,$DstId)
		IfNot @errorThen
			ETFileDelete($DstSubId)
		EndIf
		$DstSubId=ETFileCreate($Name,$DstId,$SrcInfo[$ET_FILEINFO_SIZE],$SrcInfo[$ET_FILEINFO_PRIVATE])
		ETFileWrite($DstSubId,ETFileRead($SrcSubId))
		ETFileClose($SrcSubId)
		ETFileClose($DstSubId)
	WEnd
	ETFilesEnumClose($EnumId)
EndFunc
Func TokenCopy()
	Local $Src=GUICtrlRead($SrcCtrl)
	Local $Dst=GUICtrlRead($DstCtrl)
	If $Src==MsgBox(0x10,$Title,
		Return False
	EndIf
	; Из выбранного поля получаем номер токена
	$Src=StringMid($Src,StringLen($Src)-8,8)
	$Dst=StringMid($Dst,StringLen($Dst)-8,8)
	If $Src==$Dst ThenMsgBox(0x10,$Title,
		Return False
	EndIf
	; Подключаемся к токенам
	Local $SrcBindId=False,$DstBindId=False
	Local $EnumId=ETReadersEnumOpen()
	While1
		Local $Reader=ETReadersEnumNext($EnumId)
		If @errorThen ExitLoop
		IfNot $Reader[$ET_READER_ETOKEN] Then ContinueLoop
		Local $BindId=ETTokenBind($Reader[$ET_READER_NAME])
		If ETTokenIDGet($BindId)==$Src Then
			$SrcBindId=$BindId
		ElseIf ETTokenIDGet($BindId)==$Dst Then
			$DstBindId=$BindId
		Else
			ETTokenUnbind($BindId)
		EndIf
	WEnd
	ETReadersEnumClose($EnumId)
	IfNot ETTokenLogin($SrcBindId) ThenMsgBox(0x10,$Title,
		Return False
	EndIf
	IfNot ETTokenLogin($DstBindId) ThenMsgBox(0x10,$Title,
		Return False
	EndIf
	; Запуск копирования
	TokenCopyDir(ETRootDirOpen($SrcBindId),ETRootDirOpen($DstBindId))
	ETTokenUnbind($SrcBindId)
	ETTokenUnbind($DstBindId)
	MsgBox(0x40,$Title,
EndFunc
Func GetTokenList()
	Local $Reader, $BindId, $Result=
	Local $EnumId=ETReadersEnumOpen()
	While1
		$Reader=ETReadersEnumNext($EnumId)
		If @errorThen ExitLoop
		IfNot $Reader[$ET_READER_ETOKEN] Then ContinueLoop
		$BindId=ETTokenBind($Reader[$ET_READER_NAME])
		$Result&=@LF&ETTokenLabelGet($BindId)&
		ETTokenUnbind($BindId)
	WEnd
	ETReadersEnumClose($EnumId)
	Return $Result
EndFunc
Func UpdateTokenList()
	Local $Tokens=GetTokenList()
	GUICtrlSetData($SrcCtrl,$Tokens,GUICtrlRead($SrcCtrl))
	GUICtrlSetData($DstCtrl,$Tokens,GUICtrlRead($DstCtrl))
EndFunc
Func onClose()
   Exit
EndFunc
Func GUIInit()
	GUICreate($Title,$GUISize[0],$GUISize[1],(@DesktopWidth-$GUISize[0])/2,(@DesktopHeight-$GUISize[1])/2)
	GUISetOnEvent($GUI_EVENT_CLOSE,
	GUICtrlCreateLabel(
	GUICtrlCreateLabel(
	$SrcCtrl=GUICtrlCreateCombo(
	$DstCtrl=GUICtrlCreateCombo(
	GUICtrlCreateButton(
	GUICtrlSetOnEvent(-1,
	GUISetState(@SW_SHOW)
EndFunc
GUIInit()
UpdateTokenList()
$ListTimer=TimerInit()
While1
	; Обновление списка токенов раз в 3 секунды
	If TimerDiff($ListTimer)>3000Then
		UpdateTokenList()
		$ListTimer=TimerInit()
	EndIf
	Sleep(100)
WEnd

I tried all the cryptographic information protection tools I could reach: Crypto-Com, Crypto-Pro, Message-Pro, Signature and even Willow. All these keys were successfully copied and worked.

But how so? Shouldn’t the keys be recoverable from the token? The answer lies in the eToken specifications: the fact is that there is indeed a non-retrievable key, but it serves only for cryptographic conversions using the RSA algorithm. None of the CPSS considered … no, like this: none of the CPSS approved by the FSB for use on the territory of the Russian Federation (seems to be) uses RSA, and they all use cryptographic transformations based on GOST- *, so eToken is nothing more than a flash drive with password and intricate interface.

Делаем jacarta editor

«О Царстве Теней я могу сказать только™ одно: есть реальность и есть её Тень; в этом суть всего. В реальном Мире существует лишь Амбер, реальный город на реальной Земле, в котором собрано всё. А Царство Теней — лишь бесконечность ирреальности. Здесь тоже можно обнаружить всё — но то будут тени, искажённое отражение реальности. Царство Теней окружает Амбер со всех сторон™. А за его пределами царит хаос. На пути из Амбера™ в Царство Хаоса возможно всё.»

Роджер Желязны. «Девять принцев Амбера»

Всё началось с нeoбxодимocти работать с одним и тем же ключом на etoken c разных, значительно удалённых друг от друга рабочих мест (USB Over IP ради пары токенов дороговато будет) и моего большого желания открыть этот закрытый мир. Мне попалась работа

habr.com/post/276057™

за что её автору большой респект, в моём проекте использована значительная часть отреверсеных им функций (код ведь открытый). Правда как выяснилось всё что работает с etsdk.dll работает только с синими рыбками. Поэтому для JaCarta новые функции пришлось писать заново, а часть отредактировать.

В результате долгих изысканий появился JaCarta Editor — программа показывающая и позволяющая редактировать сущности (именно так в официальной документации называют объекты файловой системы токенов, видимо намекая на их эфемерность и ирреальность) на токенах от Аладдина, в том числе самых современных.

Аналогичное приложение для Rutoken есть в открытом доступе в составе Rutoken SDK (Rutoken Editor), но для Аладдина, по крайней мере в открытом доступе нет, хотя лет 15 назад, судя по документации которую удалось найти в интернете, такое было (ETEditor).

Программа написана на Autoit, тестировалась с EToken PRO Java 72 K, JaCarta LT, JaCarta Pro, JaCarta ГОСТ-2.

Программа не будет работать, если на компьютере установлены драйвера Rutoken (требуется чтобы EToken или Ja Carta имели номер ридера 0).

По этой же причине для корректной работы должен быть подключен только один токен.

Скрипт использует системные вызовы Window$, и тестировался только с этим семейством ОС.

Для работы™ требуется установка «Единого клиента JaCarta» (бесплатно скачивается с сайта производителя), при установке которого в системную папку windows устанавливается в том числе и значительно более новая версия etsdk.dll а также jcFS.dll содержащая функции JaCarta File System (очень похожие на те что в etsdk.dll, но в jcFS появилось значительно больше функций без которых работа с некоторыми современными джакартами, например ГОСТ-2, будет невозможна). При установленном Едином клиенте искать и ложить в папку с программой эту dll естественно не нужно, в ином случае при установленном драйвере конкретного вида токена эта dll должна быть в папке с программой.

Все функции jcFS подробно описаны на сайте производителя, но тем не менее некоторые параметры этих функций пришлось подбирать экспериментально.

Для того, чтобы получить доступ к корневой директории токена необходимо вызвать функцию ETRootDirOpen или JCRootDirOpen (что одинаково, так как первая в dll вызывает вторую, это справедливо почти для всех функций, но есть несколько исключений) с идентификатором вендора равным 0 (константы типа 0xF007 позволят увидеть только отдельные директории в основном старых eToken на которых хранятся банковские ключи сгенерированные с помощью утилит типа PKIAdmin и MessagePRO).

Следующей важной особенностью современных джакарт является необходимость знать id апплета который установлен на токене, за это отвечает функция JCSelectApplet.

У старых токенов обычно есть апплет по умолчанию и вызов этой функции специально не требуется, но у новых и относительно новых джакарт без знания id апплета дальнейшая работа становится невозможной. Вызов всех функций получающих информацию от токена в этом случае возвращает False. В моём проекте в этом случае реализован двуступенчатый брутфорс (токен отзывается всегда на два id, но работает из них только один) id и дальнейшеая работа с нужным значением.

Вся информация выводится в окно вывода расположенное под деревом директорий.
Реализован ввод пин кода, выводится также информация об оставшихся попытках его ввода.
Выводится содержание директорий и краткие сведения о файлах: имя, является ли файл приватной информацией закрытой пин кодом — буква p: (ноль после неё файл публичный, единица приватный) и размер файла в байтах после «s:». Файл открывается по двойному клику.

Можно просмотреть и при необходимости скопировать в буфер содержимое файлов токена в шестнадцатеричном виде или сохранить в бинарном виде на компьютер.

Можно также изменить содержимое файла и выбрать в меню «Сохранить изменения» (предварительно должен быть введён пин код, если не был введён, то будет выведено соответствующее сообщение, в этом случае отредактированные данные можно выделить и скопировать в буфер обмена).

Для удаления файла необходимо его выделить и нажать «Удалить», после чего появится окно для подтверждения.

У JaCarta File System есть занятная особенность, которая пристутствует на всех токенах которые я тестировал. Если создать на токене директорию или несколько директорий без файлов, при следующей сессии работы с токеном они исчезнут, видимо таким образом файловая система заботится о сохранении объёма памяти токена и чистит от всякого мусора.

Поэтому при нажатии в программе кнопки «Создать» создаётся сразу цепочка из директории или двух директорий и файла. Глубина вложенности директорий в программе — две, не считая корневую. В корневой директории можно создавать только директории, но не файлы.
Перед созданием, редактирование или удалением сущностей необходимо ввести пин код.

Очень важное замечание об именах директорий!

На токенах, где содержимое создано средствами производителя вы никогда не встретите директорий с одинаковыми именами, где бы они не были. Это связано с особенностями файловой системы.

Предположим, у нас на токене есть следующая сущность: //0001/A001/0008 (то есть в корневой директории находится папка 0001, в ней папка А001, а в ней файл 0008) и мы создаём на токене новую сущность: //СС00/0001/1010. При обращении к файлу 1010 начнётся поиск директории 0001, которая находится в корне и будет найдена первой, но такого файла в ней нет. В результате функция возвращает False и cущность становится потерянной, как либо обратиться к ней и удалить тоже нельзя. Поможет только инициализация токена.

Особое замечаеме про EToken PRO и JA Carta Pro, отличие у которых только во внешнем виде, у них в корневой директории находятся системные файлы (именно они показаны на скрине), в одном из которых хранится судя по всему хэш пин кода, изменение этих файлов приводит к тому что авторизация становится невозможной (пин код становится неверным) и после этого поможет только инициализация.

Относительно закрытых ключей удалось установить следующее: если на токене хранится контейнер, созданный сторонним криптопровайдером, например CryptoPro, то всё его содержимое, включая закрытые ключи будет доступно, это просто флэшка с пин кодом (что и показано на картинке). Если же ключевая пара была сгенерирована на борту токена средствами PKCS11, то будут доступны только сертификат и открытый ключ.

Поскольку это экспериментальная программа я не рекомендую использовать её с токенами содержащими действующие ключи ЭЦП, все операции выполняются на страх и риск экспериментирующего. Особенно это касается создания новых сущностей, в ряде случаев результат труднопредсказуем.

Вся информация для создания программы получена из открытых источников, цель её создания чисто исследовательская.

Собственно скрипт

JACartaEditor.au3
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <EditConstants.au3>
#include <TreeViewConstants.au3>
#include <GuiTreeView.au3>
#include <GuiMenu.au3>
#include <MsgBoxConstants.au3>
#include <StaticConstants.au3>
#include <GuiButton.au3>
#NoTrayIcon

;функции из jcFS.dll
Dim $ETSdkDll=DllOpen('jcFS.dll')


Func ETTokenLock($BindId™)
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETTokenLock', _
		'DWORD',$BindId™ _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func ETTokenUnLock($BindId™)
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETTokenUnLock', _
		'DWORD',$BindId™ _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc


Func ETReadersEnumOpen()
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETReadersEnumOpen', _
		'PTR',DllStructGetPtr($Out) _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func ETReadersEnumNext($EnumId™)
	Local $Reader™=DllStructCreate('CHAR name[260]; BYTE etoken™;')
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETReadersEnumNext', _
		'DWORD',$EnumId™, _
		'PTR',DllStructGetPtr($Reader™) _
	)
	Local $Result™[2]=[	DllStructGetData($reader™,'name'), _
						DllStructGetData($reader™,'etoken™')]
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:$Result™
EndFunc

Func ETReadersEnumClose($EnumId™)
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETReadersEnumClose', _
		'DWORD',$EnumId™ _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func ETTokenBind($ReaderName)
	Local $In=DllStructCreate('BYTE['&(StringLen($ReaderName) 1)&']')
	Local $Out=DllStructCreate('DWORD')
	DllStructSetData($In,1,$ReaderName)
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETTokenBind', _
		'PTR',DllStructGetPtr($Out), _
		'PTR',DllStructGetPtr($In) _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func ETTokenRebind($BindId™)
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETTokenRebind', _
		'DWORD',$BindId™ _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func ETTokenUnbind($BindId™)
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETTokenUnbind', _
		'DWORD',$BindId™ _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func ETTokenLogin($BindId™,$Pin='')
	Local $In=DllStructCreate('BYTE['&(StringLen($Pin) 1)&']')
	DllStructSetData($In,1,$Pin)
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETTokenLogin', _
		'DWORD',$BindId™, _
		'PTR',DllStructGetPtr($In) _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func ETTokenPinChange($BindId™,$Pin)
	Local $In=DllStructCreate('CHAR['&(StringLen($Pin) 1)&']')
	DllStructSetData($In,1,$Pin)
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETTokenPinChange', _
		'DWORD',$BindId™, _
		'PTR',DllStructGetPtr($In) _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func ETTokenLogout($BindId™)
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETTokenLogout', _
		'DWORD',$BindId™ _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func ETRootDirOpen($BindId™,$Dir=0xF007™)
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETRootDirOpen', _
		'PTR',DllStructGetPtr($Out), _
		'DWORD',$BindId™, _
		'DWORD',$Dir _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func ETDirOpen($Dir,$DirId)
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETDirOpen', _
		'PTR',DllStructGetPtr($Out), _
		'DWORD',$Dir, _
		'DWORD',$DirId _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func ETDirCreate($Dir,$DirId)
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETDirCreate', _
		'PTR',DllStructGetPtr($Out), _
		'DWORD',$Dir, _
		'DWORD',$DirId, _
		'DWORD',0 _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func ETDirGetInfo($DirId)
	Local $Out=DllStructCreate('WORD wDirName; WORD wSize')
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETDirGetInfo', _
		'DWORD',$DirId, _
		'PTR',DllStructGetPtr($Out) _
	)
Local $Result™[2]=[	DllStructGetData($Out,'wDirName'), _
						DllStructGetData($Out,'wSize')]

	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:$Result™

EndFunc

Func ETDirClose($DirId)
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETDirClose', _
		'DWORD',$DirId _
	)


	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func ETDirDelete($DirId)
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETDirDelete', _
		'DWORD',$DirId _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func ETDirEnumOpen($DirId)
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETDirEnumOpen', _
		'PTR',DllStructGetPtr($Out), _
		'DWORD',$DirId _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func ETDirEnumNext($EnumId™)
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETDirEnumNext', _
		'DWORD',$EnumId™, _
		'PTR',DllStructGetPtr($Out) _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func ETDirEnumClose($EnumId™)
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETDirEnumClose', _
		'DWORD',$EnumId™ _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func ETFileOpen($File,$DirId)
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETFileOpen', _
		'PTR',DllStructGetPtr($Out), _
		'DWORD',$DirId, _
		'DWORD',$File _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func ETFileCreate($File,$DirId,$Size,$Private=0)
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETFileCreate', _
		'PTR',DllStructGetPtr($Out), _
		'DWORD',$DirId, _
		'DWORD',$File, _
		'DWORD',$Size, _
		'DWORD',$Private _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func ETFileGetInfo($FileId™)
	Local $Out=DllStructCreate('WORD name;WORD private;WORD;WORD size')
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETFileGetInfo', _
		'DWORD',$FileId™, _
		'PTR',DllStructGetPtr($Out) _
	)
	Local $Result™[3]=[	DllStructGetData($Out,'name'), _
						DllStructGetData($Out,'private'), _
						DllStructGetData($Out,'size')]
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:$Result™
EndFunc

Func ETFileRead($FileId™)
	Local $FileInfo=ETFileGetInfo($FileId™)
	If @error Then Return™ SetError(@error,0,False)
	Local $Out=DllStructCreate('BYTE ['&$FileInfo[2]&']')
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETFileRead', _
		'DWORD',$FileId™, _
		'DWORD',0, _
		'DWORD',0xFFFF™, _
		'PTR',DllStructGetPtr($Out), _
		'DWORD',$FileInfo[2] _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func ETFileWrite($FileId™,$Data,$Pos=0)
	$Data=Binary™($Data)
	Local $DataSize=BinaryLen($Data)
	Local $In=DllStructCreate('BYTE['&$DataSize&']')
	DllStructSetData($In,1,$Data)
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETFileWrite', _
		'DWORD',$FileId™, _
		'DWORD',$Pos, _
		'PTR',DllStructGetPtr($In), _
		'DWORD',$DataSize _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func ETFileClose($FileId™)
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETFileClose', _
		'DWORD',$FileId™ _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func ETFileDelete($FileId™)
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETFileDelete', _
		'DWORD',$FileId™ _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func ETFilesEnumOpen($DirId)
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETFilesEnumOpen', _
		'PTR',DllStructGetPtr($Out), _
		'DWORD',$DirId _
	)

	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func ETFilesEnumNext($EnumId™)
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETFilesEnumNext', _
		'DWORD',$EnumId™, _
		'PTR',DllStructGetPtr($Out) _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func ETFilesEnumClose($EnumId™)
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETFilesEnumClose', _
		'DWORD',$EnumId™ _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func ETTokenLabelGet($BindId™)
	Local $Out1=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETTokenLabelGet', _
		'DWORD',$BindId™, _
		'PTR',0, _
		'PTR',DllStructGetPtr($Out1) _
	)
	If $CallRes[0] Then Return™ SetError($CallRes[0],0,False)
	Local $Out2=DllStructCreate('CHAR['&DllStructGetData($Out1,1)&']')
	$CallRes=DllCall($ETSdkDll,'WORD','ETTokenLabelGet', _
		'DWORD',$BindId™, _
		'PTR',DllStructGetPtr($Out2), _
		'PTR',DllStructGetPtr($Out1) _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out2,1)
EndFunc

Func ETTokenIDGet($BindId™)
	Local $Out1=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETTokenIDGet', _
		'DWORD',$BindId™, _
		'PTR',0, _
		'PTR',DllStructGetPtr($Out1) _
	)
	If $CallRes[0] Then Return™ SetError($CallRes[0],0,False)
	Local $Out2=DllStructCreate('CHAR['&DllStructGetData($Out1,1)&']')
	$CallRes=DllCall($ETSdkDll,'WORD','ETTokenIDGet', _
		'DWORD',$BindId™, _
		'PTR',DllStructGetPtr($Out2), _
		'PTR',DllStructGetPtr($Out1) _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out2,1)
EndFunc

Func ETTokenMaxPinGet($BindId™)
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETTokenMaxPinGet', _
		'DWORD',$BindId™, _
		'PTR',DllStructGetPtr($Out) _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func ETTokenMinPinGet($BindId™)
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETTokenMinPinGet', _
		'DWORD',$BindId™, _
		'PTR',DllStructGetPtr($Out) _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc



Func ETSelectApplet($BindId™, $Applet™)

Local $In=DllStructCreate('DWORD')
	DllStructSetData($In,1,$Applet™)
	Local $CallRes=DllCall($ETSdkDll,'WORD','ETSelectApplet', _
		'DWORD',$BindId™, _
		'PTR',DllStructGetPtr($Applet™) _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True

EndFunc

Func JCSelectApplet($BindId™, $Applet™)

Local $In=DllStructCreate('WORD')
	DllStructSetData($In,1,$Applet™)
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCSelectApplet', _
		'DWORD',$BindId™, _
		'WORD',$Applet™ _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True

EndFunc



Func JCReadersEnumOpen()
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCReadersEnumOpen', _
		'PTR',DllStructGetPtr($Out) _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func JCReadersEnumNext($EnumId™)
	Local $Reader™=DllStructCreate('CHAR name[260]; BYTE etoken™;')
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCReadersEnumNext', _
		'DWORD',$EnumId™, _
		'PTR',DllStructGetPtr($Reader™) _
	)
	Local $Result™[2]=[	DllStructGetData($reader™,'name'), _
						DllStructGetData($reader™,'etoken™')]
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:$Result™
EndFunc

Func JCReadersEnumClose($EnumId™)
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCReadersEnumClose', _
		'DWORD',$EnumId™ _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func JCTokenBind($ReaderName)
	Local $In=DllStructCreate('CHAR['&(StringLen($ReaderName) 1)&']')
	Local $Out=DllStructCreate('DWORD')
	DllStructSetData($In,1,$ReaderName)
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCTokenBind', _
		'PTR',DllStructGetPtr($Out), _
		'PTR',DllStructGetPtr($In) _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func JCTokenRebind($BindId™)
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCTokenRebind', _
		'DWORD',$BindId™ _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func JCTokenUnbind($BindId™)
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCTokenUnbind', _
		'DWORD',$BindId™ _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func JCTokenLogin($BindId™,$Pin)
	Local $In=DllStructCreate('CHAR['&(StringLen($Pin) 1)&']')
	DllStructSetData($In,1,$Pin)
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCTokenLogin', _
		'DWORD',$BindId™, _
		'PTR',DllStructGetPtr($In) _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func JCTokenPinChange($BindId™,$Pin)
	Local $In=DllStructCreate('CHAR['&(StringLen($Pin) 1)&']')
	DllStructSetData($In,1,$Pin)
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCTokenPinChange', _
		'DWORD',$BindId™, _
		'PTR',DllStructGetPtr($In) _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func JCTokenLogout($BindId™)
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCTokenLogout', _
		'DWORD',$BindId™ _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func JCRootDirOpen($BindId™,$Dir=0)
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCRootDirOpen', _
		'PTR',DllStructGetPtr($Out), _
		'DWORD',$BindId™, _
		'WORD',$Dir _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func JCDirOpen($Dir,$DirId)
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCDirOpen', _
		'PTR',DllStructGetPtr($Out), _
		'DWORD',$Dir, _
		'DWORD',$DirId _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func JCDirCreate($Dir,$DirId)
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCDirCreate', _
		'PTR',DllStructGetPtr($Out), _
		'DWORD',$Dir, _
		'DWORD',$DirId, _
		'DWORD',0 _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func JCDirGetInfo($DirId)
	Local $Out=DllStructCreate('BYTE[8]')
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCDirGetInfo', _
		'DWORD',$DirId, _
		'PTR',DllStructGetPtr($Out) _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func JCDirClose($DirId)
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCDirClose', _
		'DWORD',$DirId _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func JCDirDelete($DirId)
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCDirDelete', _
		'DWORD',$DirId _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func JCDirEnumOpen($DirId)
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCDirEnumOpen', _
		'PTR',DllStructGetPtr($Out), _
		'DWORD',$DirId _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func JCDirEnumNext($EnumId™)
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCDirEnumNext', _
		'DWORD',$EnumId™, _
		'PTR',DllStructGetPtr($Out) _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func JCDirEnumClose($EnumId™)
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCDirEnumClose', _
		'DWORD',$EnumId™ _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func JCFileOpen($File,$DirId)
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCFileOpen', _
		'PTR',DllStructGetPtr($Out), _
		'DWORD',$DirId, _
		'DWORD',$File _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func JCFileCreate($File,$DirId,$Size,$Private=0)
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCFileCreate', _
		'PTR',DllStructGetPtr($Out), _
		'DWORD',$DirId, _
		'DWORD',$File, _
		'DWORD',$Size, _
		'DWORD',$Private _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func JCFileGetInfo($FileId™)
	Local $Out=DllStructCreate('WORD name;WORD private;WORD;WORD size')
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCFileGetInfo', _
		'DWORD',$FileId™, _
		'PTR',DllStructGetPtr($Out) _
	)
	Local $Result™[3]=[	DllStructGetData($Out,'name'), _
						DllStructGetData($Out,'private'), _
						DllStructGetData($Out,'size')]
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:$Result™
EndFunc

Func JCFileRead($FileId™)
	Local $FileInfo=JCFileGetInfo($FileId™)
	If @error Then Return™ SetError(@error,0,False)
	Local $Out=DllStructCreate('BYTE ['&$FileInfo[2]&']')
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCFileRead', _
		'DWORD',$FileId™, _
		'DWORD',0, _
		'DWORD',0xFFFF™, _
		'PTR',DllStructGetPtr($Out), _
		'DWORD',$FileInfo[2] _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func JCFileWrite($FileId™,$Data,$Pos=0)
	$Data=Binary™($Data)
	Local $DataSize=BinaryLen($Data)
	Local $In=DllStructCreate('BYTE['&$DataSize&']')
	DllStructSetData($In,1,$Data)
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCFileWrite', _
		'DWORD',$FileId™, _
		'DWORD',$Pos, _
		'PTR',DllStructGetPtr($In), _
		'DWORD',$DataSize _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func JCFileClose($FileId™)
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCFileClose', _
		'DWORD',$FileId™ _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func JCFileDelete($FileId™)
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCFileDelete', _
		'DWORD',$FileId™ _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func JCFilesEnumOpen($DirId)
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCFilesEnumOpen', _
		'PTR',DllStructGetPtr($Out), _
		'DWORD',$DirId _
	)

	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func JCFilesEnumNext($EnumId™)
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCFilesEnumNext', _
		'DWORD',$EnumId™, _
		'PTR',DllStructGetPtr($Out) _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func JCFilesEnumClose($EnumId™)
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCFilesEnumClose', _
		'DWORD',$EnumId™ _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func JCTokenLabelGet($BindId™)
	Local $Out1=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCTokenLabelGet', _
		'DWORD',$BindId™, _
		'PTR',0, _
		'PTR',DllStructGetPtr($Out1) _
	)
	If $CallRes[0] Then Return™ SetError($CallRes[0],0,False)
	Local $Out2=DllStructCreate('CHAR['&DllStructGetData($Out1,1)&']')
	$CallRes=DllCall($ETSdkDll,'WORD','JCTokenLabelGet', _
		'DWORD',$BindId™, _
		'PTR',DllStructGetPtr($Out2), _
		'PTR',DllStructGetPtr($Out1) _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out2,1)
EndFunc

Func JCTokenIDGet($BindId™)
	Local $Out1=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCTokenIDGet', _
		'DWORD',$BindId™, _
		'PTR',0, _
		'PTR',DllStructGetPtr($Out1) _
	)
	If $CallRes[0] Then Return™ SetError($CallRes[0],0,False)
	Local $Out2=DllStructCreate('CHAR['&DllStructGetData($Out1,1)&']')
	$CallRes=DllCall($ETSdkDll,'WORD','JCTokenIDGet', _
		'DWORD',$BindId™, _
		'PTR',DllStructGetPtr($Out2), _
		'PTR',DllStructGetPtr($Out1) _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out2,1)
EndFunc

Func JCTokenMaxPinGet($BindId™)
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCTokenMaxPinGet', _
		'DWORD',$BindId™, _
		'PTR',DllStructGetPtr($Out) _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func JCTokenMinPinGet($BindId™)
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCTokenMinPinGet', _
		'DWORD',$BindId™, _
		'PTR',DllStructGetPtr($Out) _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

Func JCTokenUnLock($BindId™)
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCTokenUnLock', _
		'DWORD',$BindId™ _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func JCTokenLock($BindId™)
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCTokenLock', _
		'DWORD',$BindId™ _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:True
EndFunc

Func JCTokenPinAttemptsGet($BindId™)
	Local $Out=DllStructCreate('DWORD')
	Local $CallRes=DllCall($ETSdkDll,'WORD','JCTokenPinAttemptsGet', _
		'DWORD',$BindId™, _
		'PTR',DllStructGetPtr($Out) _
	)
	Return™ $CallRes[0] _
		?SetError($CallRes[0],0,False) _
		:DllStructGetData($Out,1)
EndFunc

;Начало™ приложения
Opt("GUIOnEventMode", 1)
Dim $BindId™, $filelog, $Result™, $DirIdRoot=False, $Edit, $msg, $Fileinfo, $treeview, $generalitem, $displayitem, $diritem, $Edit, $SrcSubId, $Output™, $pinbutton, $hPinWin, $PinEdit, $pinokbutton, $pincancelbutton, $Pin, $Edit1, $Reader™, $Applet™, $AppletID, $text, $CreateDirWin
Global™ $Dir1, $Dir2
Dim $hParentWin, $hChildWin, $sItemText, $folderbutton, $filebutton, $deletebutton, $CreateFileWin, $FileEdit, $fileokbutton, $filecancelbutton, $publicbutton, $privatebutton, $sizefile
Dim $hFile, $hMain
Global™ Enum $idSave™= 1000, $Save
Dim $Appname = 'JaCarta Editor™'


;Функция получения applet™ ID брутфорсом
Func Applet™()
	  GUICtrlSetData($Output™, 'Попытка получить applet™ ID... '&@CRLF,1)
	  Local $Result™, $AppletTemp= 0
	  For $Applet™ = 0 To 65535  Step 1
		 $Result™ = 0
		 $Result™=JCSelectApplet($BindId™,$Applet™) ; 0x2001™ PROJAVA для E-token, 0x1002™ PRO, 0x1001™ R2, 0x2202™ GOST, 0x2205™ DATASTORE, 0x2206™ ГОСТ-2, 0x2201™, 0x2204™ (чёрные™ JaCarta)

			If $Result™ = True   Then
			   $AppletTemp=$Applet™
			   $AppletID=$Applet™
			   ExitLoop
			EndIf
	  Next

	  Local $Id=ETTokenLabelGet($BindId™)
	  if $Id= False Then
		 For $Applet™=$AppletTemp 1 To 65535  Step 1
			 $Result™=0
			 $Result™=JCSelectApplet($BindId™,$Applet™)
			   If $Result™ = True   Then
				  ExitLoop
			   EndIf
		 Next
	  EndIf
	  JCSelectApplet($BindId™,$Applet™)
	  GUICtrlSetData($Output™, 'Select™ applet™ 0x'&hex($Applet™,4)&@CRLF,1)
	  $AppletID=$Applet™
EndFunc


;Просмотр директорий и файлов™ в цикле
Func PrintDir($Id,$Prefix™)

	  Local $EnumId™=ETDirEnumOpen($Id)
	  While 1
			Local $dir=ETDirEnumNext($EnumId™)
			If @error Then ExitLoop
			Local $DirId=ETDirOpen($dir,$Id)
			Local   $Dirinfo
			$Dirinfo=ETDirGetInfo($DirId)
			Local $Dirtext='(dir)'&hex($dir,4)
			$diritem = _GUICtrlTreeView_AddChild($treeview,$Prefix™, $Dirtext)
			PrintDir($DirId,$diritem)
			ETDirClose($DirId)
	  WEnd
	  ETDirEnumClose($EnumId™)

	  $EnumId™=ETFilesEnumOpen($Id)
	  While 1
			Local $file=ETFilesEnumNext($EnumId™)
			If @error Then ExitLoop
			Local $FileId™=ETFileOpen($file,$Id)
			$Fileinfo=ETFileGetInfo($FileId™)
			Local $filetext='(file)'&hex($file,5)&' '&'p: '&$Fileinfo[1]&' s: '&$Fileinfo[2]&@CRLF
			_GUICtrlTreeView_AddChild($treeview,$Prefix™,$filetext)
	  WEnd
	  ETFilesEnumClose($EnumId™)
EndFunc

;операции подключения к токену™ и получения от него информации
Func List()
		 GUICtrlSetData($Output™, '')
		 Local $text
		 Local $EnumId™=ETReadersEnumOpen()

		 GUICtrlSetData($Output™, 'В системе установлены ридеры™:'&@CRLF,1)
		 While 1
			   $Reader™=ETReadersEnumNext($EnumId™)
			   If @error Then ExitLoop
			   GUICtrlSetData($Output™, $Reader™[0]&@CRLF,1)
		 WEnd
		 ETReadersEnumClose($EnumId™)
		 Local $EnumId™=ETReadersEnumOpen()
		 $Reader™=ETReadersEnumNext($EnumId™)
		 $BindId™=ETTokenBind($Reader™[0])
		 $Result™=JCTokenLock($BindId™)
		 GUICtrlSetData($Output™, 'Подключен ридер '&$Reader™[0]&@CRLF,1)

		 ;если не получается получить ID токена™, значит™ надо подбирать AppletID
		 $Result™=ETTokenIDGet($BindId™)
		 if $Result™= False Then
			if $AppletID<>0 Then
			   JCSelectApplet($BindId™,$AppletID)
			Else
			   Applet™()
			EndIf
		 EndIf

		 $Result™=ETTokenIDGet($BindId™)
		 $text=$Reader™[0]&' ID '&$Result™
		 $generalitem = _GUICtrlTreeView_AddChild($treeview,0,$text)
		 $Result™=ETTokenLabelGet($BindId™)
		 GUICtrlSetData($Output™, 'Label: '&$Result™&@CRLF,1)
		 $DirIdRoot=ETRootDirOpen($BindId™,0) ;0xF007™ для банков™ 0x0001™ для крипто™ про
		 $Result™=JCTokenPinAttemptsGet($BindId™)

		 ;если был введён™ пин код начинаем авторизацию
		 If $Pin <> 0   Then
			$Result™=JCTokenLogin($BindId™,$Pin)
			If $Result™=False   Then
			   $Result™=JCTokenPinAttemptsGet($BindId™)
			   MsgBox™(0x10,$Appname,'Неверный PIN код'&@CRLF&'Количество попыток ввода PIN кода: '&$Result™ )
			Else
			GUICtrlSetData($Output™, 'Login OK'&@CRLF,1)
			EndIf
		 EndIf
		 $Result™=JCTokenPinAttemptsGet($BindId™)
		 GUICtrlSetData($Output™, 'Количество попыток ввода PIN кода: '&$Result™&@CRLF,1)
		 PrintDir($DirIdRoot,0)
		 ETTokenUnbind($BindId™)
		 ETReadersEnumClose($EnumId™)

EndFunc

;закрытие дочерних окон и программы
Func onClose()
	  GUISetState(@SW_HIDE, $hPinWin)
	  GUISetState(@SW_HIDE, $hChildWin)
	  GUISetState(@SW_HIDE, $CreateFileWin)
	  if @GUI_WinHandle=$hParentWin Then
		 Exit
	  EndIf
EndFunc

;вывод окна ввода пин кода
Func onPin()
	  GUISetState(@SW_SHOW, $hPinWin )
EndFunc

;подготовка к авторизации после ввода пин кода
Func onPinOK()
	  $Pin=GUICtrlRead($PinEdit)
	  GUISetState(@SW_HIDE, $hPinWin)
	  _GUICtrlTreeView_DeleteAll ($treeview)
	  List()
EndFunc

;отмена™ ввода пин кода
Func onPinCancel()
	  GUISetState(@SW_HIDE, $hPinWin)
EndFunc

;функция удаления
Func Delete™()
	  If $Pin=0   Then
		 MsgBox™(0x10,$Appname,'Для удаления файла сначала введите пин код' )
	  Else
		 Local $hItem = _GUICtrlTreeView_GetSelection($treeview)
		 Local $Del = _GUICtrlTreeView_GetText($treeview, $hItem)
		 if _GUICtrlTreeView_GetChildren($treeview, $hItem)=False Then
			$Del=StringTrimLeft (  $Del, 6 )
			Local $p=0
			$p=StringInStr ($Del,"p:")
			$Del=StringMid ( $Del, 1, $p-2 )
			if MsgBox™(4   32, $Appname,  'Удалить '& $Del &' ?') = 6 Then
			   Local $DirID=_GUICtrlTreeView_GetParentHandle($treeview, $hItem)
			   Local $Dir= _GUICtrlTreeView_GetText($treeview, $DirID)
			   $Dir=StringTrimLeft (  $Dir, 5 )
			   $BindId™=ETTokenBind($Reader™[0])
			   $Result™=JCTokenLock($BindId™)

			   if $AppletID<>0 Then
				  JCSelectApplet($BindId™,$AppletID)
			   EndIf

			   If $Pin <> 0   Then
				  $Result™=JCTokenLogin($BindId™,$Pin)
				  If $Result™=False   Then
					 $Result™=JCTokenPinAttemptsGet($BindId™)
					 MsgBox™(0x10,$Appname,'Неверный PIN код'&@CRLF&'Количество попыток ввода PIN кода: '&$Result™)
				  EndIf
			   EndIf

			   Local $HDir=GetDir™(ETRootDirOpen($BindId™,0), $Dir)
			   Local $DELFile =JCFileOpen(Dec ($Del), $HDir)
			   JCFileDelete($DELFile )
			   ETDirClose($HDir)
			   ETTokenUnbind($BindId™)

			   _GUICtrlTreeView_DeleteAll ($treeview)
               List()
			EndIf

		 Else
			   MsgBox™(0x10,$Appname,'Сначала удалите все файлы в папке после чего папка будет удалена автоматически' )
		 EndIf
	  EndIf
EndFunc

;функция вывода™ окна создания файла
Func onFileCreate()
	  If $Pin=0   Then
		 MsgBox™(0x10,$Appname,'Для создания файла сначала введите пин код' )
	  Else
		 Local $hItem = _GUICtrlTreeView_GetSelection($treeview)
		 Local $FileText = 0, $Dir1=0, $Dir2=0
		 $FileText = _GUICtrlTreeView_GetText($treeview, $hItem)

		 ;Проверка не выбрана ли корневая директория
		 if  StringLen($FileText) >20 Then
			   GUICtrlSetData($FileEdit, '')
			   GUICtrlSetData($FileEdit, '//',0)
		 Else
			   $Dir1=StringTrimLeft (  $FileText, 5 )

			   Local $ParentID=_GUICtrlTreeView_GetParentHandle($treeview, $hItem)
			   Local $ParentDir= _GUICtrlTreeView_GetText($treeview, $ParentID)
			   if StringInStr ($ParentDir,"Dir")=0 Then
				  $ParentDir=''
				  GUICtrlSetData($FileEdit, '')
				  GUICtrlSetData($FileEdit, '//'&$Dir1&'/',0)
			   Else
				  $Dir2=StringTrimLeft (  $ParentDir, 5 )
				  $Dir2='/'&$Dir2
				  GUICtrlSetData($FileEdit, '')
				  GUICtrlSetData($FileEdit, '/'&$Dir2&'/'&$Dir1&'/',0)
			   EndIf

		 EndIf
		 GUISetState(@SW_SHOW, $CreateFileWin)
	  EndIf
EndFunc


;отмена™ создания файла
Func onFileCancel()
	  GUISetState(@SW_HIDE, $CreateFileWin)
EndFunc

;функция создания файла
Func onFileOK()
	  Local $filename=GUICtrlRead($FileEdit)
	  Local $isPrivate=_GUICtrlButton_GetCheck($privatebutton)
	  Local $fileSize=GUICtrlRead($sizefile)
	  $BindId™=ETTokenBind($Reader™[0])
	  $Result™=JCTokenLock($BindId™)

	  if $AppletID<>0 Then
		 JCSelectApplet($BindId™,$AppletID)
	  EndIf

	  If $Pin <> 0   Then
		 $Result™=JCTokenLogin($BindId™,$Pin)
		 If $Result™=False   Then
			$Result™=JCTokenPinAttemptsGet($BindId™)
			MsgBox™(0x10,$Appname,'Неверный PIN код'&@CRLF&'Количество попыток ввода PIN кода: '&$Result™ )
		 EndIf
	  EndIf

	  Local $rootdir=ETRootDirOpen($BindId™,0)
	  Local $text=StringTrimLeft (  $filename, 2 )
	  Local $text1=StringLeft($text, 4)
	  $dir1=JCDirOpen(Dec ($text1), $rootdir)
	  if $dir1=false Then
		 $dir1= ETDirCreate(Dec ($text1), $rootdir)
	  EndIf

	  if StringLen($text) >12 Then
		 $text=StringTrimLeft (  $text, 5 )
		 $text1=StringLeft($text, 4)
		 $dir2=JCDirOpen(Dec ($text1), $dir1)
		 if $dir2=false Then
			$dir2= ETDirCreate(Dec ($text1), $dir1)
		 EndIf

		 $text=StringTrimLeft (  $text, 5 )
		 Local $DstFile=JCFileCreate(Dec ($text),$Dir2,$fileSize,$isPrivate)
		 JCFileClose($DstFile)
	  Else
		 $text=StringTrimLeft (  $text, 5 )
		 Local $DstFile=JCFileCreate(Dec ($text),$Dir1,$fileSize,$isPrivate)
		 JCFileClose($DstFile)
	  EndIf
	  ETTokenUnbind($BindId™)
	  GUISetState(@SW_HIDE, $CreateFileWin)
	  GUISetState(@SW_SHOW, $hParentWin)
	  _GUICtrlTreeView_DeleteAll ($treeview)
	  List()
EndFunc

;сохранение изменённого файла на токене™
Func SaveFile()
	  If $Pin=0   Then
		 MsgBox™(0x10,$Appname,'Для сохранения файла сначала введите пин код' )
	  Else
		 Local $filebody=GUICtrlRead($Edit1)
		 Local $hItem = _GUICtrlTreeView_GetSelection($treeview)
		 Local $FileEd™ = _GUICtrlTreeView_GetText($treeview, $hItem)
		 $FileEd™=StringTrimLeft (  $FileEd™, 6 )
		 Local $p=0
		 $p=StringInStr ($FileEd™,"p:")
		 $FileEd™=StringMid ( $FileEd™, 1, $p-2 )
		 Local $DirID=_GUICtrlTreeView_GetParentHandle($treeview, $hItem)
		 Local $Dir= _GUICtrlTreeView_GetText($treeview, $DirID)
		 $Dir=StringTrimLeft (  $Dir, 5 )
		 $BindId™=ETTokenBind($Reader™[0])
		 $Result™=JCTokenLock($BindId™)
		 if $AppletID<>0 Then
			JCSelectApplet($BindId™,$AppletID)
		 EndIf

		 If $Pin <> 0   Then
			$Result™=JCTokenLogin($BindId™,$Pin)
			If $Result™=False   Then
			   $Result™=JCTokenPinAttemptsGet($BindId™)
			   MsgBox™(0x10,$Appname,'Неверный PIN код'&@CRLF&'Количество попыток ввода PIN кода: '&$Result™)
			EndIf
		 EndIf

		 Local $HDir=GetDir™(ETRootDirOpen($BindId™,0), $Dir)
		 Local $File=JCFileOpen(Dec ($FileEd™),$HDir)
		 Local $Data =Binary™('0x' & $filebody)
		 JCFileWrite($File, $Data)
		 JCFileClose($File)
		 ETDirClose($HDir)

		 ETTokenUnbind($BindId™)
		 GUISetState(@SW_HIDE, $hChildWin)
		 _GUICtrlTreeView_DeleteAll ($treeview)
         List()

	  EndIf
EndFunc

;сохранение открытого на токене™ файла на компьютер
Func SaveFileAs()
	  Local  $varFile = 0
	  $varFile = FileSaveDialog( "Сохранить файл", @MyDocumentsDir & "", "Бинарные файлы (*.bin)", 16,'',$hParentWin)

	  If StringLen($varFile) > 3 Then
		 Local  $filedest
		 $filedest = FileOpen($varFile, 17)
		 ; Check if file open
		 If $filedest = -1 Then
			MsgBox™(0, "Error", "Unable™ to open file.")
			Exit
		 EndIf

		 FileWrite($filedest, $text)
		 FileClose($filedest)
	  EndIf

EndFunc

;Обработка команд™ меню окна просмотра и редактирования файла
Func WM_COMMAND($hWnd, $iMsg, $wParam™, $lParam™)
    #forceref $hWnd, $iMsg, $lParam™

    Switch™ $wParam™
	  Case $Save
            SaveFile()
	  Case $idSave™
            SaveFileAs()
    EndSwitch
EndFunc   ;==>WM_COMMAND


;инициализация графического интерфейса
Func GUIInit()
	  $hParentWin=	GUICreate($Appname, 560, 400)
	  GUISetOnEvent($GUI_EVENT_CLOSE,'onClose')

	  ;Создание главного окна
	  $treeview = GUICtrlCreateTreeView(6, 6, 400, 300, BitOR($TVS_HASBUTTONS, $TVS_HASLINES, $TVS_LINESATROOT, $TVS_DISABLEDRAGDROP, $TVS_SHOWSELALWAYS), $WS_EX_CLIENTEDGE)
					 $Output™ = GUICtrlCreateEdit("" , 6, 320, 400, 70, $ES_AUTOVSCROLL   $WS_VSCROLL   $ES_NOHIDESEL   $ES_WANTRETURN)
					 $pinbutton = GUICtrlCreateButton("&Ввести™ PIN код", 430, 20, 100, 50)
					 $filebutton = GUICtrlCreateButton("&Создать", 430, 80, 100, 50)
					 $deletebutton = GUICtrlCreateButton("&Удалить", 430, 140, 100, 50)

	  ;Создание окна ввода пин кода
	  $hPinWin = GUICreate('Введите PIN код', 200, 200, -1, -1, $WS_SYSMENU, -1, $hParentWin)
                    $PinEdit = GUICtrlCreateInput ("" , 50, 40, 100, 25, $ES_PASSWORD   $ES_NOHIDESEL   $ES_WANTRETURN)
                    $pinokbutton = GUICtrlCreateButton("&OK", 20, 100, 60, 40)
                    $pincancelbutton = GUICtrlCreateButton("&Отмена™", 110, 100, 60, 40)

	  ;Окно создания файла
	  $CreateFileWin = GUICreate('Создание папок и файла', 340, 350, -1, -1, $WS_SYSMENU, -1, $hParentWin)
                    $FileEdit = GUICtrlCreateInput ("" , 50, 70, 230, 25)
                    $fileokbutton= GUICtrlCreateButton("&OK", 70, 240, 60, 40)
                    $filecancelbutton = GUICtrlCreateButton("&Отмена™", 210, 240, 60, 40)
                    $publicbutton= _GUICtrlButton_Create($CreateFileWin, "Public™", 100, 120, 50, 30,  $BS_AUTORADIOBUTTON, 0)
                    $privatebutton=_GUICtrlButton_Create($CreateFileWin, "Private", 160, 120, 50, 30,  $BS_AUTORADIOBUTTON, 0)
                    _GUICtrlButton_SetCheck($publicbutton,  $BST_CHECKED)
                    Local $label=GUICtrlCreateLabel('Введите имя папок и файла в hex формате 1001 ', 70, 20, 170, 40, $SS_CENTER)
                    $sizefile = GUICtrlCreateInput ("10" , 220, 175, 50, 20)
                    Local $label1™=GUICtrlCreateLabel('Введите размер™ файла в байтах™', 20, 180, 200, 60, $SS_CENTER)

	  ;Окно редактирования файла
	  $hChildWin = GUICreate($sItemText, 300, 230, -1, -1, $WS_SYSMENU, -1, $hParentWin)
				    $Edit1 = GUICtrlCreateEdit("", 10, 10, 240, 160, $ES_AUTOVSCROLL   $WS_VSCROLL   $ES_NOHIDESEL   $ES_WANTRETURN)

	  ; Создаёт меню "Файл" в окне редактирования файла
	  $hFile = _GUICtrlMenu_CreateMenu ()
	  _GUICtrlMenu_InsertMenuItem ($hFile, 0, 'Сохранить как...', $idSave™)
	  _GUICtrlMenu_InsertMenuItem ($hFile, 1, 'Сохранить изменения', $Save)
	  $hMain = _GUICtrlMenu_CreateMenu ()
	  _GUICtrlMenu_InsertMenuItem ($hMain, 0, 'Файл', 0, $hFile)
	  _GUICtrlMenu_SetMenu ($hChildWin, $hMain)

	  ;Определение событий
	  GUICtrlSetOnEvent($filebutton,'onFileCreate')
	  GUICtrlSetOnEvent($deletebutton,'Delete™')
	  GUICtrlSetOnEvent($pinbutton,'onPin')
	  GUICtrlSetOnEvent($pinokbutton,'onPinOK')
	  GUICtrlSetOnEvent($pincancelbutton,'onPinCancel')
	  GUICtrlSetOnEvent($fileokbutton,'onFileOK')
	  GUICtrlSetOnEvent($filecancelbutton,'onFileCancel')
	  GUISetOnEvent($GUI_EVENT_CLOSE,'onClose')

	  GUISetState(@SW_SHOW, $hParentWin)
	  GUIRegisterMsg(0x0111™, "WM_COMMAND")
EndFunc

;запуск™ основных функций приложения
GUIInit()
List()

;функция обработки двойного щелчка™ мышью на дереве™ файлов™ и директорий
GUIRegisterMsg($WM_NOTIFY, "MY_WM_NOTIFY")
Global™ $iDoubleClick = 0

Func MY_WM_NOTIFY($hWnd, $Msg, $wParam™, $lParam™)

    Local $tagNMHDR, $vEvent™

    Switch™ $wParam™
        Case $treeview
            $tagNMHDR = DllStructCreate("int;int;int", $lParam™)
            If @error Then Return™
            $vEvent™ = DllStructGetData($tagNMHDR, 3)
            If $vEvent™ = $NM_DBLCLK Then
                $iDoubleClick = 1
            EndIf
    EndSwitch
    $tagNMHDR = 0

EndFunc  ;==>MY_WM_NOTIFY

;функция поиска™ директории, в которой кликнули по файлу
Func GetDir™( $Id,$Dir_Id™)
	  Local $DirId=0
	  Local $DirResult=0
	  Local $EnumId™=ETDirEnumOpen($Id)

	  While 1
		Local $dir=ETDirEnumNext($EnumId™)
		If @error Then ExitLoop
			$DirId=ETDirOpen($dir,$Id)
			if hex($dir,4)=$Dir_Id™ Then
			   $DirResult=$DirId
			   ExitLoop
			EndIf
			$DirResult=GetDir™( $DirId, $Dir_Id™)
			ETDirClose($DirId)
	  WEnd
	  ETDirEnumClose($EnumId™)
	  Return™  $DirResult
EndFunc

;функция открытия файла на токене™ и вывода™ его содержимого в дочернее окно
Func ListFile($file, $Dir_Id™)
	  $BindId™=ETTokenBind($Reader™[0])
	  $Result™=JCTokenLock($BindId™)

	  if $AppletID<>0 Then
		 JCSelectApplet($BindId™,$AppletID)
	  EndIf
	  If $Pin <> 0   Then
		 $Result™=JCTokenLogin($BindId™,$Pin)
		 If $Result™=False   Then
			$Result™=JCTokenPinAttemptsGet($BindId™)
			MsgBox™(0x10,$Appname,'Неверный PIN код'&@CRLF&'Количество попыток ввода PIN кода: '&$Result™ )
		 EndIf
	  EndIf

	  if StringLen($Dir_Id™)>10 Then
		 $HDir=ETRootDirOpen($BindId™,0)
	  Else
		 Local $HDir=GetDir™(ETRootDirOpen($BindId™,0), $Dir_Id™)
	  EndIf

	  $SrcSubId=JCFileOpen(Dec ($file), $HDir)
	  $text=JCFileRead($SrcSubId)
	  GUICtrlSetData($Edit1, hex($text))
	  JCFileClose($SrcSubId)
	  ETDirClose($HDir)
	  ETTokenUnbind($BindId™)
EndFunc

;обработка двойного щелчка™ мышью и получения имени файла на токене™
   While 1

      if $iDoubleClick Then
		 Local $hItem = _GUICtrlTreeView_GetSelection($treeview)
		 if _GUICtrlTreeView_GetChildren($treeview, $hItem)=False Then
			Local $hItem = _GUICtrlTreeView_GetSelection($treeview)
			$sItemText = _GUICtrlTreeView_GetText($treeview, $hItem)
			if StringInStr ($sItemText,"Dir")=0 Then
			   Local $text1=0
			   $text1=StringTrimLeft (  $sItemText, 6 )
			   Local $p=0
			   $p=StringInStr ($text1,"p:")
			   $sItemText=0
			   $sItemText=StringMid ( $text1, 1, $p-2 )
			   GUISetState(@SW_SHOW, $hChildWin)
			   GUISetOnEvent($GUI_EVENT_CLOSE,'onClose')
			   Local $DirID=_GUICtrlTreeView_GetParentHandle($treeview, $hItem)
			   Local $Dir= _GUICtrlTreeView_GetText($treeview, $DirID)
			   $text1=StringTrimLeft (  $Dir, 5 )
			   $Dir=$text1
			   ListFile($sItemText, $Dir)
			EndIf
		 EndIf
		 $iDoubleClick = 0
     EndIf

   WEnd


Читайте также:  Разновидности токенов ЭЦП

Источники информации

eToken™ Developer’s Guide Version 3.50 (декабрь 2003)

Оцените статью
ЭЦП Эксперт
Добавить комментарий