If you learn assembly with problems to guide you, analyze patiently, I believe the gains will be significant, which helps to understand the logic and methods of assembly operations.
Let’s start today’s problem explanation; the question will be uploaded in a package. If there are any errors, please point them out.
一
x86汇编
I won’t paste the original problem here; it’s in the attachment. Here I’ll paste my annotated analysis. The problem provides AT&T style x86 assembly, so how does it differ from the Intel syntax we are familiar with?
1. Operand order differs: In AT&T syntax, the order of operands is “source, destination”, whereas in Intel syntax, it is “destination, source”. For example, in AT&T syntax, <span>movl %eax, %ebx</span>
means moving the value from the <span>eax</span>
register to the <span>ebx</span>
register, while in Intel syntax, the corresponding instruction is <span>mov ebx, eax</span>
.
2. Register names differ: In AT&T syntax, register names start with <span>%</span>
, while in Intel syntax, there is no prefix. For example, in AT&T syntax, <span>%eax</span>
represents the EAX register, while in Intel syntax, the corresponding register name is <span>EAX</span>
.
3. Immediate values and memory address representations differ: In AT&T syntax, immediate values use a <span>$</span>
prefix, while memory addresses are enclosed in square brackets <span>[]</span>
. For example, <span>movl $0x123, %eax</span>
means moving the immediate value <span>0x123</span>
to the <span>eax</span>
register, while <span>movl (%ebx), %eax</span>
means moving data from the memory address pointed to by the <span>ebx</span>
register into the <span>eax</span>
register.
4. Sign extension differs: In AT&T syntax, sign extension is performed by default, while in Intel syntax, it requires using <span>movsx</span>
or <span>movzx</span>
instructions. For example, in AT&T syntax, <span>movb -1(%eax), %bl</span>
means moving the byte at the address <span>eax-1</span>
after sign extension into the <span>bl</span>
register, while in Intel syntax, the corresponding instruction would be <span>movsx bl, byte ptr [eax-1]</span>
.(It’s recommended to first look at the original problem, analyze it manually, and then try to decrypt to see if you can get the correct flag.)
With the above foundation, let’s start looking at the problem:
```@ Define some auxiliary information, such as: file name, section name
.file "main.c"
.text
.section .rodata
.align 32
.type encode, @object
.size encode, 39
encode:
.string "**************************************"
.text
.globl main @ Mark main as globally visible.
.type main, @function @ Declare main as a function.
main:
.LFB0:
.cfi_startproc @ CFI (Call Frame Information) directive, used to generate debugging information.
endbr64 @ Corresponds to specific processor instruction, providing overflow detection for branch target addresses.
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp @ rsp=rsp-16
@ Stack operation instructions, used to save and restore register values. The above, excluding CFI instructions, is classic stack space allocation instructions
movl $0, -4(%rbp) @ [rbp-4]=0
jmp .L2 @ Unconditional jump to L2 to start execution
@ L5 From the analysis below, it can be known that this is a loop
.L5:
addl $1, -4(%rbp) @[rbp-4]++
.L2:
movl -4(%rbp), %eax @ eax=[rbp-4]
cltq @ Sign extend the low 32 bits of eax to 64 bits, storing the result in the rax register (in x86-64, eax is the low 32 bits of rax).
leaq encode(%rip), %rdx @ rdx=encode[rip] This passes the flag to rdx
movzbl (%rax,%rdx), %eax @ Read a byte from the memory address based on rax+rdx, zero-extend it to 32 bits. Then store the result in the eax register
movsbl %al, %ecx @ ECX = EAX
movl -4(%rbp), %eax @ eax = [rbp-4]
andl $1, %eax @ Check odd/even
testl %eax, %eax
je .L3 @ If even, jump to L3
movl -4(%rbp), %eax @ eax=[rbp-4]
subl $1, %eax @ eax=eax-1
cltq
leaq encode(%rip), %rdx @ rdx = encode[rip]
movzbl (%rax,%rdx), %eax @ eax = [rax+rdx] rax acts like index i
movsbl %al, %eax
jmp .L4
.L3:
movl -4(%rbp), %eax @ eax=[rbp-4]
.L4:
xorl %ecx, %eax @ eax = eax ^ ecx
movl %eax, %edi @ edi = eax
call putchar@PLT @ putchar
movl -4(%rbp), %eax @ eax=[rbp-4]
cltq
cmpq $37, %rax @ cmp(37,rax)
jbe .L5 @ If rax < 37, jump to L5, continue loop
movl $0, %eax @ eax =0
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
@ Some debugging information
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:
This problem has a bit of a twist regarding the relationship between [rbp-4], eax, and ecx.
Here is the complete solution wp; the crypto ciphertext is provided in the problem attachment.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char crypto[] = { 0x66,0x0a,0x63,0x06,0x7f,0x1e,0x37,0x00,0x38,0x03,0x6f,0x04,0x6e,0x56,0x3d,0x55,0x22,0x06,0x26,0x51,0x72,0x04,0x21,0x03,0x21,0x01,0x7c,0x05,0x2b,0x0e,0x7c,0x50,0x17,0x56,0x10,0x0b,0x16,0x4f,0x26 };
for (size_t i = 0; i < 39; i++)
{
if (i % 2 == 0)// Even
{
printf("%c", crypto[i] ^i);
}
else {
char tmp = i;
crypto[i - 1] = crypto[i - 1] ^ (i - 1);
printf("%c", crypto[i-1] ^ crypto[i]);
}
}
return 0;
}
</string.h></stdlib.h></stdio.h>
二
ARM汇编
Here is a brief introduction to ARM assembly, taken from my personal notes.
ARM assembly is a low-level programming language aimed at the ARM architecture, used for writing programs closely related to hardware. Here are some characteristics of ARM assembly:
1. Reduced Instruction Set: The ARM architecture is known for its Reduced Instruction Set Computing (RISC). Its instruction set is designed to be simple and efficient, providing basic arithmetic, logical, data transfer, and flow control operations.
2. Registers: The ARM architecture has a large number of general-purpose registers. The 32-bit ARM architecture (ARMv7 and ARMv8) provides 16 general-purpose registers (R0-R15) for storing commonly used data and temporary calculation results.
3. Three-Address Instruction Format: ARM instructions typically use a three-address instruction format, meaning the instruction includes source operands, destination operands, and the operation to perform. This makes ARM assembly relatively concise and reduces memory access times.
4. Thumb Instruction Set: On some ARM processors, there is also a Thumb instruction set that uses 16-bit instructions (compared to 32-bit ARM instructions) to reduce code size. The Thumb instruction set can provide higher code density, suitable for memory-constrained embedded systems.
5. Cross-Platform: The ARM architecture is widely used in mobile devices, embedded systems, and low-power applications, including smartphones, tablets, and IoT devices. Due to the diversity of the ARM processor family, ARM assembly can be shared among different ARM processors, but there will also be some differences and specific instructions.
Here is the annotated assembly of the problem:
@ ARM gcc 11.1(linux)
.Ltext0:
flag:
.ascii "***************************\000"
.LC0:
.ascii "%02x\000"
main:
.LFB0:
@ This is a comment indicating that the function takes no parameters, uses an 8-byte stack frame, and needs to save the context data.
@ args = 0, pretend = 0, frame = 8
@ frame_needed = 1, uses_anonymous_args = 0
push {r7, lr} @ Save the values of r7 and lr registers to the stack for later restoration.
sub sp, sp, #8 @ Allocate 8 bytes of space on the stack.
add r7, sp, #0 @ Save the stack pointer to r7 register, forming a frame pointer.
.LBB2:
movs r3, #0
str r3, [r7, #4] @ Assign 0 to r3 register and store it at the frame pointer offset of 4.
b .L2 @ Unconditional jump to label .L2.
.L5: @ This is a label marking the start of a loop (the reason it's a loop will be analyzed later)
ldr r3, [r7, #4]
and r3, r3, #1
cmp r3, #0
bne .L3
@ Load a value from the frame pointer offset of 4 into r3 register and perform a bitwise AND with 1. Then compare the result with 0, and if not equal, jump to label .L3.
movw r3, #:lower16:flag @ Load the lower 16 bits address of the global variable flag into r3 register.
movt r3, #:upper16:flag @ Load the upper 16 bits address of the global variable flag into the high 16 bits of r3 register.
ldr r2, [r7, #4] @ Load a value from the frame pointer offset of 4 into r2 register. This value may be an offset used to correct the address in r3 register.
add r3, r3, r2 @ Add the value in r2 register to r3 register to correct the address of flag.
ldrb r3, [r3] @ zero_extendqisi2 Load a value from the address in r3 register and zero-extend it. This value is the value of flag.
eor r3, r3, #57 @ Perform a bitwise XOR operation with the value in r3 register and 57.
uxtb r3, r3 @ Zero-extend the value in r3 register to a byte.
mov r1, r3 @ Copy the value in r3 register to r1 register as the second parameter for the printf function.
movw r0, #:lower16:.LC0 @ Load the string similarly
movt r0, #:upper16:.LC0
bl printf @ Call the function
b .L4 @ Unconditional jump
.L3: @ flag[i] is odd, do not operate
movw r3, #:lower16:flag
movt r3, #:upper16:flag
ldr r2, [r7, #4]
add r3, r3, r2 @ The purpose here is to take the index flag[i]
ldrb r3, [r3] @ zero_extendqisi2
mov r1, r3 @
movw r0, #:lower16:.LC0
movt r0, #:upper16:.LC0
bl printf @ Call the function
.L4: @ Load a value from the frame pointer offset of 4 into r3 register and add 1. Then save the result back to the frame pointer offset of 4
ldr r3, [r7, #4]
adds r3, r3, #1
str r3, [r7, #4]
.L2: @ Load a value from the frame pointer offset of 4 into r3 register and compare it with 26. If r3 is less than or equal to 26, jump to label .L5, otherwise continue executing.
ldr r3, [r7, #4]
cmp r3, #26
ble .L5
.LBE2: @ Assign 0 to r3 register and store it as the return value in r0 register, increase the value of frame pointer and set the stack pointer to the frame pointer
movs r3, #0
mov r0, r3
adds r7, r7, #8
mov sp, r7
@ sp needed
pop {r7, pc}
.LFE0:
.Letext0:
.Ldebug_info0:
.Ldebug_abbrev0:
.Ldebug_line0:
.LASF6:
.LASF10:
.LASF0:
.LASF11:
.LASF3:
.LASF7:
.LASF9:
.LASF1:
.LASF15:
.LASF8:
.LASF12:
.LASF2:
.LASF14:
.LASF5:
.LASF13:
.LASF4:
@ 7d416a436d4642315c740c64095f7872745f6d720d315769574744
Write the corresponding decryption script:
#include <stdio.h>
char flag[] = { 0x7d,0x41,0x6a,0x43,0x6d,0x46,0x42,0x31,0x5c,0x74,0x0c,0x64,0x09,0x5f,0x78,0x72,0x74,0x5f,0x6d,0x72,0x0d,0x31,0x57,0x69,0x57,0x47,0x44 }; // The string content here should match the actual flag in the code
int main() {
int i = 0;
while (i <= 26) {
if ((i & 1) != 0) { // If i is odd, output directly
printf("%c", flag[i]);
} else {
printf("%c", flag[i] ^ 57);
}
i++;
}
return 0;
}
</stdio.h>
As you can see, both problems check for odd/even, unlike previous ones which were simply XOR or addition/subtraction. This way of checking odd/even further tests the contestants’ understanding of the assembly process.
Kanxue ID: NYSECbao
https://bbs.kanxue.com/user-home-971547.htm

# Previous Recommendations
1、IOFILE exploit introduction
2、Experience of the front end of compiler principles
3、How to reverse the encryption of Himalaya xm files using pure guessing (wasm part)
4、How AMSI helps you defend against malware
5、sRDI — Shellcode reflective DLL injection technology
6、Detection and parameter calculation analysis of APP
