Help on getting and setting Extended Attribute of file windows.

I am trying to classify the file so that even the file is sent out to another PC, the DLP still can track the file if DLP software is installed on the target PC as well. So to classify the file I am querying and setting the extended Attribute of the file. But getting an error, I don’t know whether missing something or what.

Successfully opened the file but getting errors on ZwSetEaFile and ZwQueryEaFile

Following errors once I run the code:
On querying the Extended Attribute(EA): System error 1392 has occurred. The file or directory is corrupted and unreadable.
On setting the Extended Attribute(EA): System error 255 has occurred. The extended attributes are inconsistent.

Really Need some helping hand. I will be very thankful to you.

Note: My code is in DriverEntry

Attached code:

        NTSTATUS status;
	WCHAR filepathtoread[] = L"\\??\\\\E:\\test.txt";
	char header[10] = "EAdemo";
	LONG eaLength;
	PFILE_FULL_EA_INFORMATION eaBuffer = NULL; // to set
	PFILE_FULL_EA_INFORMATION pGetEA; // to get
	char Buffer[sizeof(FILE_FULL_EA_INFORMATION) + 20];
	IO_STATUS_BLOCK IoStatus;
	UNICODE_STRING str;
	OBJECT_ATTRIBUTES obj;
	HANDLE FileHandle;
	RtlInitUnicodeString(&str, filepathtoread);
	InitializeObjectAttributes(&obj, &str,OBJ_CASE_INSENSITIVE, NULL, NULL); // object attributes from file path

	status = NtOpenFile(&FileHandle,
		GENERIC_READ | FILE_READ_EA | FILE_WRITE_EA | SYNCHRONIZE,
		&obj,
		&IoStatus,
		0,
		FILE_SYNCHRONOUS_IO_NONALERT);

	if (!NT_SUCCESS(status)) {
		DbgPrint("EA:Error in open file: "); // Don't Close handle here because of invalid file handle
		return status;
	}
	DbgPrint("EA:Success in open file: ");

	eaLength = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) + sizeof(header) + 1;
	eaBuffer = (PFILE_FULL_EA_INFORMATION)ExAllocatePoolWithTag(NonPagedPool, eaLength, '1EBA');

	if (NULL == eaBuffer) {
		DbgPrint("EA:Error in ExAllocatePoolWithTag ");
		NtClose(FileHandle);
		return status;
	}

	RtlZeroMemory(eaBuffer, eaLength);

	eaBuffer->NextEntryOffset = 0;
	eaBuffer->Flags = 0;
	eaBuffer->EaNameLength = (UCHAR)strlen(header);
	eaBuffer->EaValueLength = (USHORT)strlen(header);

	RtlCopyMemory(&eaBuffer->EaName[0], header, strlen(header) + 1);

	status = ZwSetEaFile(FileHandle,
		&IoStatus,
		eaBuffer,
		eaLength);

	if (!NT_SUCCESS(status)) {
		DbgPrint("EA:Error in ZwSetEaFile ");
		NtClose(FileHandle);
		return status;
	}
	DbgPrint("EA: EA set success.. Now Reading.. ");

	status = ZwQueryEaFile(FileHandle,
		&IoStatus,
		(PFILE_FULL_EA_INFORMATION)Buffer,
		sizeof(Buffer),
		TRUE,
		NULL,
		0,
		NULL,
		TRUE);

	if (!NT_SUCCESS(status)) {
		DbgPrint("EA:Error in ZwQueryEaFile ");
		NtClose(FileHandle);
		return status;
	}

	pGetEA = (PFILE_FULL_EA_INFORMATION)Buffer;

	DbgPrint("EA:EA is %S\n", pGetEA->EaName[0]);

	NtClose(FileHandle);

It won’t help, but

  • your eaLength calculation is wrong, you haven’t allowed space for the data so the write is over reading, you would have crashed under verify
  • It would be nice to know the NTSTATUS values not the post-digested Windows error
  • The +1 is totally suspicious. In Windows all strings are counted not zero terminated.

None if which explain the behavior

@rod_widdowson The NTSTATUS value is STATUS_EA_LIST_INCONSISTENT on calling ZwSetEaFile. can you please tell me the correct calculation for eaLength? I am lost now. Thank you very much for the info.

eaLength = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) + sizeof(header) + 1;
The length has to be large enough for

  • The header part: FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0])
  • The name: sizeof(header)
  • The data: + 1;

but you are setting the data length to be eaBuffer->EaValueLength = (USHORT)strlen(header);

Try setting the eaLength to FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) + sizeof(header) + sizeof(header) or eaBuffer->EaValueLength = 1

@rod_widdowson Thank you so much for clearing it to me. I have got the point now. You saved my day. Thanks a lot.

Let’s not make that hasty statement. Event in kernel mode that is not
true, and it is completely false in user mode.

On 10/10/20, rod_widdowson wrote:
> OSR https://community.osr.com/
> * The +1 is totally suspicious. In Windows all strings are counted not zero
> terminated.

@rod_widdowson How we are storing more than 1 bytes in EaName as EaName is an array of a single character.
Look at this line:

    RtlCopyMemory(&eaBuffer->EaName[0], header, strlen(header) + 1);

and 1 more thing since attributes are key/value pair so where can we store the value of a key.
I don’t see any value field in FILE_FULL_EA_INFORMATION structure.
FILE_FULL_EA_INFORMATION structure for your reference:

typedef struct _FILE_FULL_EA_INFORMATION {
  ULONG  NextEntryOffset;
  UCHAR  Flags;
  UCHAR  EaNameLength;
  USHORT EaValueLength;
  CHAR   EaName[1];
} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION;

Please suggest a way to do this.
I need your expertise in this regard.
Thank you so much.

You need to read the Fine manual

The value(s) associated with each entry follows the EaName array. That is, an EA’s values are located at EaName + (EaNameLength + 1).

ok got it. Thanks a lot.