Age Verification on Android and iOS, Sideloading for Verified Apps Only

Google is making some changes to Android that make it more restrictive than I’ve previously thought of it. Age verification for apps is coming to Android, and side loading is being restricted to apps that have been “verified” by Google. This is something that is coming to conform to new laws. Apple is also making some changes to conform and published information about it earlier this year. I just received Android’s notification about the change. The full e-mail is appended to the end of this post.

App Verification for Sideloaded Apps

Sideloading is the practice of loading applications onto the device outside of an app store. For Android, this usually involves changing a setting on the phone to allow applications from unknown sources, copying a .apk file to the device, and opening it for the phone to install the app within the .apk. To make an app, someone could download Android Studio, write their code, package their app, and share it with their friends at no cost. Google says that a side-loaded application is 50 times more likely to contain malware. For a developer to distribute their app outside of the Play Store, the developer must register with Google. This is a change from being able to build an app and make the .apk available without interacting with Google. Users who do not load applications outside of the Play Store will not see a difference.

Age Verification

Age verification is attributed to several state laws. The most prominent one referenced is Texas’s Senate Bill 2420. This bill says it regulates the sale of applications to mobile devices. It creates an obligation for app stores to inquire and verify user ages and categorize users into one of a defined set of categories.

  • Age 13 or older but younger than 16
  • At least 16 but younger than 18
  • 18 or older

For each application that a minor downloads, the download will require the consent of the parent. The developers are obligated to come up with age ratings for the applications based on the categories above. The developer must disclose information on the elements of the software that lead to a particular setting being selected. Safety-related features must be enabled in response to the younger age categories. The Texas bill also says that the parent of a minor can make civil damage claims against a developer or app store for failing to meet the requirements.

Expected Impacts

My expectation is that a typical end-user is not likely to notice any change from this bill. At most, there’s the possibility of apps from smaller independent developers that may disappear if it is not eventually updated to conform. From what I’ve seen of the children in my family, they are more inclined to install new apps, especially games. I expect parents to be bugged a lot more about giving permission to install apps. That said, it isn’t unusual for a child to have access to their parents’ phones and sometimes certain passwords. I can’t help but expect that some non-significant portion of children may just use their parents’ phones or passwords to approve themselves.

Different than 1996

These restrictions remind me of another set of restrictions from 29 years ago. In 1996, Congress passed a bipartisan bill, the Communications and Decency Act. A part of that bill required that any website that may have content that isn’t appropriate for children perform age verification and filtering. Most of this bill failed and was enjoined as unconstitutional. The bill’s requirement for age verification would burden lawfully speaking adults and non-commercial interactions. The only portion of that bill that survives as a law today is a law that many simply refer to as Section 230 (47 USC §230). §230 provides a civil defense from liability for what someone else posted to an interactive computer service. The implementation of the Texas bill and others differs from the 1996 Act in that it targets commercial entities (App stores).

The Email from Google

What’s happening

A few U.S. states, currently Texas, Utah, and Louisiana, have recently passed verification laws requiring app stores to verify users’ ages, obtain parental approval, and provide users’ age information to developers. These laws also create new obligations for developers who distribute their apps through app stores in these states.

Our plan to support you

While we have user privacy and trust concerns with these new verification laws, Google Play is designing APIs, systems, and tools to help you meet your obligations. The first verification law to take effect is Texas’s SB 2420 on January 1, 2026. Given short implementation timelines, we are sharing details about the Play Age Signals API (beta) and have made the API integration guide available to you.

What this means for you

These laws impose significant new requirements on many apps that may need to provide age appropriate experiences to users in these states. These requirements include ingesting users’ age ranges and parental approval status for significant changes from app stores and notifying app stores of significant changes. It is important that you review these laws and understand your obligations.

If you have any additional questions, please contact our support team.

Thank you,
Your Google Play team


Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Bluesky: @j2i.net

Running Code in the Pre-Boot Environment

Before your operating system loads, the UEFI (Unified Extensible Firmware Interface) runs. The code in the UEFI is responsible for getting the initial bits of your operating system loaded and then passes control to it for it to load the rest of its components. For the sake of having a better understanding of how things work, this past weekend I decided to write code that would run from the UEFI. Unless you are making system components, this is something that would be impractical to do in the general case. But curious exploration isn’t constrained by practicality. There do exist SDKs for making UEFI programs. Visual Studio also supports the UEFI as a binary target. I’m not using any of the SDKs though. I won’t need them for what I’m doing. To get as far as a “Hello World” I’ll only use Intel Assembler. I wouldn’t suggest that someone that doesn’t already know x86 assembler try this.

Configuring a Visual Studio Project for Assembler

Yes, I’ve not forgotten that I’ve published a video saying that to generally avoid programming in assembler. This isn’t going in any serious code, I don’t have that concern here. From that video, though, I did have instructions on how to create the appropriate type of project in Visual Studio and modify it so that it can compile assembler code.

To use Visual Studio for Assembler development, you will want to create a C++ project. C++ projects support mixing C++ and assembly. We will make a C++ project where 0% of the code is in C++ and 100% is in assembly. In Visual Studio you can create an empty C++ project. Add a new file to the project named main.asm. Right now, VS will not do anything useful with that file. There are two changes that must be made. One is that MASM (Microsoft Assembler) targets must be enabled for the project. The other, MASM must be set as the target for your ASM files. To enable MASM as a target first click on your project in the Solution three, then navigate in Visual Studio to the menu selection “Projects” and then “Build Customizations.” Check the box next to “masm(.targets .props)” and click on “OK.”

To set the target type for main.asm you will need to manually update the project file. right-click on your project in the solution tree and select “Unload project.” Search for “main.asm” in the file. You will find a line that looks like this.

<None Include="main.asm" />

Change the word “None” to “MASM” so that it looks like this.

<MASM Include="main.asm" />

Right-click on the project file and select “Reload project.” Visual Studio able to compile now. But it will try to make a Windows application. That’s not what we want. We want a UEFI application. Visual C++ must be set to target UEFI as the subsystem type. Right-click on the project file and select “Properties.” At the top, change the Configuration setting to “All Configurations.” In the settings tree on the left, navigate to Configuration -> Linker -> System. Change the SubSystem setting to “EFI Application.” Now, select the configuration path Configuration -> Linker -> Advanced. Here, set the “Entry Point” setting to “main”, set “Randomize Base Address” to “No”, and set “Data Execution Prevention” to “No”.

Visual Studio will produce an executable that is the same name as the project but with the EXE extension. That’s not what we want. We want the file name to be BOOTX64.efi. To set that, navigate to the settings path Configuration->Linker->General. Change the Output File setting from $(OutDir)$(TargetName)$(TargetExt) to $(OutDir)BOOTX64.efi. With that, all the configuration steps are complete. Now, we need to write code.

Creating our First Program

This first program will be incredibly simple. It does nothing but loop for some number of cycles and then it terminates. That’s it. Why make a program so simple? Debugging is a little more complex for EFI programs and won’t be covered here. In the absence of a debugger, we will make a program that is as simple as possible while also having effect so that it is known that the program ran. Without relying on any external functionality, the most observable thing that the program can do is take up execution cycles. I do this with the assembler equivalent of

for(auto i=0;i<0x1000000;++i)
{
}

While the program is running, the screen is black. When it finishes running, the UEFI will take over. I can run the program with larger or smaller values in the loop to observe longer or shorter periods of time with a black screen, letting me know that it ran. Here is the source code.

.DATA
; no data
.CODE
main PROC
     MOV RCX, 01000000000H
delay_loop:
	DEC RCX
	JNZ delay_loop
	XOR RAX, RAX
	RET
main ENDP

This code loads a register with a large number. It then loops, decrementing the register value and continuing to do so until that register value has reached zero. When it has, the program sets the RAX register to zero and returns control to the UEFI. The UEFI might check the value of RAX since it is set to a non-zero value if a problem occurred. Compile this and copy the output to a USB key in the folder /EFI/BOOT. It is ready to run!

Running the Program

Usually in Visual Studio, you just press [F5] and your program will compile and run. That’s not an option here. The program must be in a pre-boot environment to run. The easiest way to run the code is either from another computer or from a virtual machine. Attempting to run it on your development machine would mean that you would need to reboot. An emulator or a second machine lets you avoid that. I’m using VM Ware Workstation, which is now available for free from VMWare’s site. ( https://www.vmware.com/products/desktop-hypervisor/workstation-and-fusion ). In any case, you’ll want to ensure that “Secure Boot” is turned off. If Secure Boot is turned on, the computer will refuse to run your code because it isn’t signed with keys that the computer trusts. In VMWare Workstation, right-click on the VM that you plan to use for testing and select “Settings.” In the two tabs at the top of the window that appears, select “Options” and then select “Advanced.” Ensure the firmware type is set to UEFI and that “Secure Boot” is not checked. Click on Okay.

Power-On the Virtual Machine. Once it is powered on, In the “VM” menu select “Removable Devices.” You should see your USB drive. Select it and choose the “Connect Option.” The drive will appear to be unplugged from your computer and connected to the host machine.

Now select the option to reboot the VM.

When the machine reboots, you should see the screen remain black for a bit before the UEFI menu shows. During this period when it is black, the program is looping. If you shut down the VM then the USB drive will become visible to your computer again.

Writing Text

The UEFI has APIs available for reading and writing text, graphics, and more. Let’s try to write text to the screen. We will write “Hello World”, delay, and then return control to the system. This first time that we do that we will be applying knowledge that is known about the system that cannot be inferred from looking at the code alone. When our program starts, two registers already have pointers to a couple of items of information that we need. The RCX register (64-bit general purpose register) has a handle to some Image information. The RDX register has a pointer to the System Table. The system table contains pointers to objects and functions that we will want to use and provides other information about the system. At an offset 0x40 (64) bytes into the system table is a pointer to an object known as ConOut that is used for writing text to the console. At an offset 0x8 bytes from that object’s address is a pointer to the function entry point for the function known as OutputString. We want to call that to display text on the screen. When we call this function, we need to set the RDX register to point to the address of the string that we want to print. After we print, we run our delay and then return control to the UEFI.

.DATA
     szHelloWorld DW 'H','e','l','l','o',' ','W','o','r','l','d','\r','\n', 0
.CODE
main PROC
	SUB RSP, 10H*8	
	MOV RCX, [RDX + 40H] ; Get ConOut function address
	LEA RDX, [szHelloWorld]
	CALL QWORD PTR [RCX + 08H] ;Output String	
	MOV RCX, 01000000000H
delay_loop:
	DEC RCX
	JNZ delay_loop
	ADD RSP, 10H*8	
	XOR RAX, RAX
	RET
main ENDP

END

If we run the program now, it shows text

Adding Structures for Reading Objects

Reading data from arbitrary offsets both work and results in horrible readability. The code will be a lot more readable if we read it using structs instead of arbitrary memory offset. There are three structs that we need: EFI_TABLE_HEADER, SYSTEM_TABLE, and TEXT_OUTPUT_INTERFACE.The EFI_TABLE_HEADER here is being used within the SYSTEM_TABLE struct. I could have defined it in line with SYSTEM_TABLE, but it is used by some other UEFI structures. I decided against it. Most of the other entries in the SYSTEM_TABLE are 64-bit pointers (DQ – or double quads, meaning 8 bytes). Though a few members are doubles (DD – or 32-bit numbers).

EFI_TABLE_HEADER STRUCT
  Signature		DQ ?
  Revision		DD ?
  HeaderSize	DD ?
  CRC			DD ?
  Reserved		DD ?
EFI_TABLE_HEADER ENDS

SYSTEM_TABLE STRUCT
	HDR						EFI_TABLE_HEADER <?> ; 00H
	FIRMWARE_VENDOR_PTR		DQ ? ; 18H
	FIRMWARE_REVISION_PTR	DQ ? ; 20H
	CONSOLE_INPUT_HANDLE	DQ ? ; 28H
	ConIn					DQ ? ; 30H
	CONSOLE_OUTPUT_HANDLE	DQ ? ; 28
	ConOut					DQ ? ; 30
	StandardErrorHandle		DQ ? ; 38
	STD_ERR					DQ ? ; 40
	RuntimeServices			DQ ? ; 48
	BootServices			DQ ? ; 50
	NumberOfTableEntries	DD ? ; 58H
	ConfigurationTable		DQ ? ; 60H
SYSTEM_TABLE ENDS

TEXT_OUTPUT_INTERFACE STRUCT
	Reset				DQ	?
	OutputString		DQ	?
	TestString			DQ	?
	QueryMode			DQ	?
	SetMode				DQ	?
	SetAttribute		DQ	?
	ClearScreen			DQ	?
	SetCursorPosition	DQ	?
	EnableCursor		DQ	?
	Mode				DQ	?
TEXT_OUTPUT_INTERFACE ENDS

With these structs defined, there are a few ways we now have access to access structured data. When using a register to access a field of a struct, I can specify the field offset in the MOV operation. The line of code looks like this.

[RDX + SYSTEM_TABLE.ConOut]

Adding that notation to the code, I end up with code that looks like this.

.CODE
main PROC
	SUB RSP, 10H*8	
	MOV RCX, [RDX + SYSTEM_TABLE.ConOut] ; Get ConOut function address
	LEA RDX, [szHelloWorld]
	CALL QWORD PTR [RCX + TEXT_OUTPUT_INTERFACE.OutputString] ;Output String
	
	MOV RCX, 01000000000H
delay_loop:
	DEC RCX
	JNZ delay_loop
	ADD RSP, 10H*8	
	XOR RAX, RAX
	RET
main ENDP

Reading Text

For someone that wants to play with this, it may also be helpful to be able to read text from the keyboard. Just as there is a Console Output object, there is also a Console Input object. I’ll have the code wait in a spin-loop until a key is pressed. Then it will print the key that was pressed, delay a bit, and terminate. The UEFI boot services do offer a function that will wait on system events. A key press counts as a system event. But I will stick with a spin-wait for simplicity.

I’m declaring a new procedure named waitForKey. This procedure uses a system object that implements the TEXT_INPUT_PROTOCOL. The object has the method ReadKeyStroke that communicates either that there is no keystroke available (sets the RAX register to a non-zero value) or that there is a keystroke (sets RAX register to zero) and writes the keyboard scan code and Unicode character to the memory address that it received in the RDX register. My code loops while RAX is set to non-zero.

.DATA
     szKeyRead DW ' '
.CODE
waitForKey PROC
		SUB RSP, 8
	waitForEnter_Retry:
		MOV RCX, [systemTable]
		MOV RCX, [RCX + SYSTEM_TABLE.ConIn]
		MOV RDX, RSP
		CALL QWORD PTR [ECX+TEXT_INPUT_PROTOCOL.ReadKeyStroke]
		CMP EAX, 0
		JNZ waitForEnter_Retry
		MOV AX, WORD PTR [RSP+2]
		MOV WORD PTR [szKeyRead], AX
		ADD RSP, 8
		RET
waitForKey ENDP

I’ll put the code necessary to print a string in a procedure, too. It will be called printString. The address to the zero-terminated string must be passed in the RAX register.

printString PROC
		MOV RCX, [systemTable]
		MOV RCX, [RCX + SYSTEM_TABLE.ConOut]
		MOV RDX, RAX
		CALL QWORD PTR [RCX+TEXT_OUTPUT_INTERFACE.OutputString]
		RET
printString ENDP

The code will now wait on user input before terminating.

Downloading the Code

If you want to try this out, the Visual Studio project and source code are available on GitHub. In that repository there is also a build folder that contains the binary. If you want to try it out, copy it to the path efi/BOOT on a FAT32 formatted USB drive and boot from it.

Other Resources

I used VMWare for a test device. It is available for free download from the VMWare Workstation web site. For development, I used Microsoft Visual Studio 2022. It is available for free from the Microsoft Visual Studio website. Information about the various objects that are available for use in UEFI code can be found on the site UEFI.org,


Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Bluesky: @j2i.net