Skip to content

Instantly share code, notes, and snippets.

@isopen
Created October 23, 2025 18:09
Show Gist options
  • Save isopen/c00f09d643ce65e465637f812a79695b to your computer and use it in GitHub Desktop.
Save isopen/c00f09d643ce65e465637f812a79695b to your computer and use it in GitHub Desktop.
TDLib ARM Client in ARM32 Assembly (ARMv7)
// TDLib ARM Client in ARM32 Assembly (ARMv7)
//
// https://gist.github.com/isopen/2be1547e3e98a053cbc3a26851a5ebcd
// docker build -t tdlib-arm-app .
// docker run -it --rm tdlib-arm-app
.section .data
// Error messages
lib_error: .asciz "Error: libtdjson.so not found\n"
client_error: .asciz "Error: Failed to create client\n"
input_error: .asciz "Error reading input\n"
receive_error: .asciz "Error in receive function\n"
// Function names
create_name: .asciz "td_json_client_create"
send_name: .asciz "td_json_client_send"
receive_name: .asciz "td_json_client_receive"
destroy_name: .asciz "td_json_client_destroy"
execute_name: .asciz "td_json_client_execute"
// Library name
lib_name: .asciz "libtdjson.so"
// Prompts
phone_prompt: .asciz "Enter phone number (international format): "
code_prompt: .asciz "Enter authentication code: "
password_prompt: .asciz "Enter password: "
auth_success: .asciz "Authorization successful!\n"
auth_wait: .asciz "Waiting for authorization...\n"
// JSON messages
params_msg: .asciz "{\"@type\":\"setTdlibParameters\",\"database_directory\":\"td_data\",\"use_message_database\":true,\"api_id\":94575,\"api_hash\":\"a3406de8d171bb422bb6ddf3bbd800e2\",\"system_language_code\":\"en\",\"device_model\":\"ARMv7Client\",\"application_version\":\"1.0\"}"
verbosity_msg: .asciz "{\"@type\":\"setLogVerbosityLevel\",\"new_verbosity_level\":2}"
close_msg: .asciz "{\"@type\":\"close\"}"
newline: .asciz "\n"
// Authorization states
auth_wait_phone: .asciz "authorizationStateWaitPhoneNumber"
auth_wait_code: .asciz "authorizationStateWaitCode"
auth_wait_password: .asciz "authorizationStateWaitPassword"
auth_ready: .asciz "authorizationStateReady"
// Format strings
fmt_phone: .asciz "{\"@type\":\"setAuthenticationPhoneNumber\",\"phone_number\":\"%s\"}"
fmt_code: .asciz "{\"@type\":\"checkAuthenticationCode\",\"code\":\"%s\"}"
fmt_password: .asciz "{\"@type\":\"checkAuthenticationPassword\",\"password\":\"%s\"}"
.section .bss
// Handle pointers
.align 4
lib_handle: .skip 4
client_handle: .skip 4
// Function pointers
create_func: .skip 4
send_func: .skip 4
receive_func: .skip 4
destroy_func: .skip 4
execute_func: .skip 4
// Buffers
.align 4
input_buffer: .skip 256
phone_buffer: .skip 32
code_buffer: .skip 16
password_buffer: .skip 64
json_buffer: .skip 1024
auth_state: .skip 4
.section .text
.global main
.extern dlopen
.extern dlsym
.extern printf
.extern sleep
.extern exit
.extern fgets
.extern stdin
.extern strstr
.extern sprintf
.extern strlen
.extern putchar
.extern usleep
.extern fflush
main:
push {r4, r5, r6, r7, r8, r9, r10, fp, lr}
add fp, sp, #32
// Load TDLib library
ldr r0, =lib_name
mov r1, #1
bl dlopen
cmp r0, #0
beq .dl_error
ldr r1, =lib_handle
str r0, [r1]
// Get function addresses
ldr r0, [r1]
ldr r1, =create_name
bl dlsym
ldr r1, =create_func
str r0, [r1]
ldr r0, =lib_handle
ldr r0, [r0]
ldr r1, =send_name
bl dlsym
ldr r1, =send_func
str r0, [r1]
ldr r0, =lib_handle
ldr r0, [r0]
ldr r1, =receive_name
bl dlsym
ldr r1, =receive_func
str r0, [r1]
ldr r0, =lib_handle
ldr r0, [r0]
ldr r1, =destroy_name
bl dlsym
ldr r1, =destroy_func
str r0, [r1]
ldr r0, =lib_handle
ldr r0, [r0]
ldr r1, =execute_name
bl dlsym
ldr r1, =execute_func
str r0, [r1]
// Create TDLib client
ldr r0, =create_func
ldr r0, [r0]
blx r0
cmp r0, #0
beq .client_error
ldr r1, =client_handle
str r0, [r1]
// Set log verbosity
ldr r0, =client_handle
ldr r0, [r0]
ldr r1, =verbosity_msg
ldr r2, =execute_func
ldr r2, [r2]
blx r2
// Set TDLib parameters
ldr r0, =client_handle
ldr r0, [r0]
ldr r1, =params_msg
ldr r2, =send_func
ldr r2, [r2]
blx r2
// Authorization loop
mov r7, #0
ldr r0, =auth_state
mov r1, #0
str r1, [r0]
ldr r0, =#50000
bl usleep
.auth_loop:
ldr r0, =client_handle
ldr r0, [r0]
mov r1, #0
ldr r2, =receive_func
ldr r2, [r2]
blx r2
cmp r0, #0
beq .no_message
// Process message
bl process_message
cmp r7, #1
beq .auth_complete
.no_message:
// Check if we need to request input
ldr r0, =auth_state
ldr r0, [r0]
cmp r0, #1 // wait_phone
beq .request_phone
cmp r0, #2 // wait_code
beq .request_code
cmp r0, #3 // wait_password
beq .request_password
ldr r0, =#10000
bl usleep
b .auth_loop
.request_phone:
ldr r0, =phone_prompt
bl printf
mov r0, #0
bl fflush
// Read phone number
ldr r0, =phone_buffer
mov r1, #32
ldr r2, =stdin
ldr r2, [r2]
bl fgets
cmp r0, #0
beq .input_error
// Remove newline
ldr r0, =phone_buffer
bl remove_newline
// Create JSON for phone number
ldr r0, =json_buffer
ldr r1, =fmt_phone
ldr r2, =phone_buffer
bl sprintf
// Send to TDLib
ldr r0, =client_handle
ldr r0, [r0]
ldr r1, =json_buffer
ldr r2, =send_func
ldr r2, [r2]
blx r2
ldr r0, =auth_state
mov r1, #0
str r1, [r0]
ldr r0, =#10000
bl usleep
b .auth_loop
.request_code:
ldr r0, =code_prompt
bl printf
mov r0, #0
bl fflush
// Read code
ldr r0, =code_buffer
mov r1, #16
ldr r2, =stdin
ldr r2, [r2]
bl fgets
cmp r0, #0
beq .input_error
// Remove newline
ldr r0, =code_buffer
bl remove_newline
// Create JSON for code
ldr r0, =json_buffer
ldr r1, =fmt_code
ldr r2, =code_buffer
bl sprintf
// Send to TDLib
ldr r0, =client_handle
ldr r0, [r0]
ldr r1, =json_buffer
ldr r2, =send_func
ldr r2, [r2]
blx r2
ldr r0, =auth_state
mov r1, #0
str r1, [r0]
ldr r0, =#10000
bl usleep
b .auth_loop
.request_password:
ldr r0, =password_prompt
bl printf
mov r0, #0
bl fflush
// Read password
ldr r0, =password_buffer
mov r1, #64
ldr r2, =stdin
ldr r2, [r2]
bl fgets
cmp r0, #0
beq .input_error
// Remove newline
ldr r0, =password_buffer
bl remove_newline
// Create JSON for password
ldr r0, =json_buffer
ldr r1, =fmt_password
ldr r2, =password_buffer
bl sprintf
// Send to TDLib
ldr r0, =client_handle
ldr r0, [r0]
ldr r1, =json_buffer
ldr r2, =send_func
ldr r2, [r2]
blx r2
ldr r0, =auth_state
mov r1, #0
str r1, [r0]
ldr r0, =#10000
bl usleep
b .auth_loop
.auth_complete:
ldr r0, =auth_success
bl printf
// Main loop
mov r8, #100
.main_loop:
// Receive messages
ldr r0, =client_handle
ldr r0, [r0]
mov r1, #0
ldr r2, =receive_func
ldr r2, [r2]
blx r2
cmp r0, #0
beq .no_msg_main
// Print message
bl printf
ldr r0, =newline
bl printf
.no_msg_main:
ldr r0, =#50000
bl usleep
subs r8, r8, #1
bne .main_loop
.cleanup:
// Send close command
ldr r0, =client_handle
ldr r0, [r0]
ldr r1, =close_msg
ldr r2, =send_func
ldr r2, [r2]
blx r2
ldr r0, =#50000
bl usleep
// Destroy client
ldr r0, =client_handle
ldr r0, [r0]
ldr r1, =destroy_func
ldr r1, [r1]
blx r1
// Exit
mov r0, #0
bl exit
.dl_error:
ldr r0, =lib_error
bl printf
mov r0, #1
bl exit
.client_error:
ldr r0, =client_error
bl printf
b .cleanup
.input_error:
ldr r0, =input_error
bl printf
b .cleanup
process_message:
push {r4, r5, r6, r7, r8, lr}
mov r4, r0
ldr r1, =auth_wait_phone
bl strstr
cmp r0, #0
bne .wait_phone
mov r0, r4
ldr r1, =auth_wait_code
bl strstr
cmp r0, #0
bne .wait_code
mov r0, r4
ldr r1, =auth_wait_password
bl strstr
cmp r0, #0
bne .wait_password
mov r0, r4
ldr r1, =auth_ready
bl strstr
cmp r0, #0
bne .auth_ready
b .done
.wait_phone:
ldr r0, =auth_state
mov r1, #1
str r1, [r0]
b .done
.wait_code:
ldr r0, =auth_state
mov r1, #2
str r1, [r0]
b .done
.wait_password:
ldr r0, =auth_state
mov r1, #3
str r1, [r0]
b .done
.auth_ready:
mov r7, #1
ldr r0, =auth_state
mov r1, #0
str r1, [r0]
.done:
pop {r4, r5, r6, r7, r8, pc}
remove_newline:
mov r1, r0
.find_end:
ldrb r2, [r1]
cmp r2, #0
beq .check_newline
add r1, r1, #1
b .find_end
.check_newline:
sub r1, r1, #1
ldrb r2, [r1]
cmp r2, #10
bne .end
mov r2, #0
strb r2, [r1]
.end:
bx lr
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment