Course Objective #
Why Learn C Language? #
From Reverse Engineering Perspective #
In Android application security analysis, we encounter different implementation approaches:
“We don’t approach this from a development perspective, but rather from a reverse engineering perspective. As long as we can understand and analyze, that’s sufficient. Different goals lead to different levels of learning difficulty.
Why learn C language for app reverse engineering?
Because most app algorithms are developed at the Java layer, which is relatively easy to implement. To prevent algorithms from being found through Java decompilation and to discover the specific encryption process, developers must increase the difficulty of cracking, so they use C language for development.
When encountering APKs, use JADX for decompilation, When encountering SO dynamic libraries, use IDA for decompilation.
Finally, use hooking methods to pass values and observe the value transmission process.
This is the reverse engineering perspective.
From an Android development perspective: By implementing the developer’s process, we know how this encryption was implemented.”

There is a MainActivity, and when this app first starts, there’s an onCreate function that calls a method in some class. If a certain core parameter is obtained, then calling the encryption function at the Java layer can achieve encrypted submission. However, to increase the difficulty of cracking, we need to introduce functions written in C language.
In the next section, we’ll cover JNI development, using System.loadLibrary to call a native function that is written in C language. We can see the specific implementation of the encrypt function in the crypt.c file.
Android Development Context #
If you see System.loadLibrary(“Crypt”); this code in the JADX decompiled code, it indicates that you need to find the libCrypt.so file in the app’s dynamic libraries and drag it into IDA for decompilation. By finding the exported function Java_com_yoloho_libcore_util_Crypt_encrypt_1data() in the SO file, you can roughly understand its meaning from the pseudocode. You need to understand their logic.

This course provides an introduction to C language programming concepts, specifically designed for reverse engineering and mobile security analysis. Students will learn essential C programming concepts including data types, pointers, arrays, structures, and linked lists.
1. C Language Development Environment Setup #
Three Essential Components: #
- Language Syntax: Learn C grammar and write code
- Compiler/Interpreter: Compile and run code
- IDE: Rapid development environment
1.1 Compilers #
After writing C code following the language syntax, it needs to be compiled by a compiler before execution.
#include <stdio.h>
int main(int argc, char const *argv[]) {
printf("hello world\n");
return 0;
}
Compilation in Terminal:
>>> gcc main.c
>>> ./a.out
Common C Compilers: #
- GCC: GNU Compiler Collection (most widely used)
- MSVC: Microsoft Visual C++ Compiler
- Clang: LLVM-based compiler
Platform-Specific Installation: #
macOS:
- Default: Clang compiler (built-in with Xcode command line tools)
- If not available: Install Xcode from https://developer.apple.com/xcode/
Windows:
- Recommended: MinGW (Minimalist GNU for Windows)
- Download: https://osdn.net/projects/mingw/downloads/68260/mingw-get-setup.exe
1.2 Integrated Development Environment (IDE) #
CLion by JetBrains:
- Download: https://www.jetbrains.com/clion/download/other.html
- Recommended versions: 2023.3.1 (easier licensing) or latest 2025.3.1
2. C Language Syntax Fundamentals #
2.1 String Handling #
Important Concept: C language does not have built-in string data types.
#include <stdio.h>
int main() {
// Single character
char letter = 'A'; // Only single quotes can be used here, not double quotes. This creates a character. It occupies one byte of storage.
// Character array
char word[5] = {'c', 'a', 'l', 'l', '\0'}; // "call" — the brackets [5] mean there are 5 elements. '\0' is a null terminator that marks the end of the string "call".
// Character array = string
char phrase[] = "clang-basic-lesson"; // {'c','l','a','n','g','-','b','a','s','i','c','-','l','e','s','s','o','n','\0'} — although it looks like a string, it is actually a character array at the underlying level. "phrase" represents this string.
// Print single character
printf("%c\n", letter); // %c is used for printing characters
// Print character array "word"
printf("%s\n", word); // %s is used for printing strings
// Print character array "phrase"
printf("%s\n", phrase); // %s is used for printing strings
// Print Hello, Calleng!
printf("Hello, Calleng!\n");
return 0;
}
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
// ---------------------------------------------
// 1. Character type (1 byte storage)
// ---------------------------------------------
char v1 = 'w';
printf("v1 value: %c\n", v1);
// %c is used to print a single character (not a string).
// ---------------------------------------------
// 2. Character array -> String
// ---------------------------------------------
char v2[10] = {'j', 'o', 'e', 'b', 'i', 'd', 'd', 'e', 'n', '\0'};
printf("v2 value: %s\n", v2);
// %s prints a null-terminated string (ends with '\0').
// If '\0' is missing, printf() may read beyond memory and cause errors.
// ---------------------------------------------
// 3. Character array with sizeof() calculation
// ---------------------------------------------
char v3[] = "NullFlag";
int length = sizeof(v3) / sizeof(char);
// sizeof(v3) gives the total size in bytes (includes '\0').
// Dividing by sizeof(char) gives the total number of stored characters, including the null terminator.
// So the result here is 9 (8 visible chars + 1 '\0').
printf("v3 value: %s, length: %d\n", v3, length);
// Note: sizeof() measures memory size, not logical string length.
// For logical string length (excluding '\0'), use strlen() instead.
// ---------------------------------------------
// 4. Integer array and sizeof()
// ---------------------------------------------
int ch_array[3] = {100, 200, 300};
int ch_array_size = sizeof(ch_array);
printf("ch_array size is %d\n", ch_array_size);
// Output: 12, because each int occupies 4 bytes, and 3 * 4 = 12.
// sizeof() returns the total byte size.
// To get the number of elements: sizeof(ch_array) / sizeof(int) = 3.
// ---------------------------------------------
// 5. Character array with Chinese characters (UTF-8)
// ---------------------------------------------
char Chinese_characters[] = "中文Bidden";
// In UTF-8 encoding, each Chinese character (like '中' or '文') typically occupies 3 bytes.
// "中文" = 6 bytes, "Bidden" = 6 bytes, plus '\0' = 1 byte → total 13 bytes in memory.
int len = sizeof(Chinese_characters) / sizeof(char);
printf("Chinese_characters value: %s, total bytes: %d\n", Chinese_characters, len);
// sizeof() counts the total bytes in memory, including '\0'.
// ---------------------------------------------
// 6. String length calculation with strlen()
// ---------------------------------------------
unsigned long dataLen = strlen(Chinese_characters);
printf("using function strlen() Length: %lu\n", dataLen);
// strlen() returns the number of visible characters (not including '\0').
// sizeof() counts total memory size (including '\0').
return 0; // Finally, remember: bytes, characters, character arrays, and how they are represented — these are the core concepts you must understand first when learning strings.
}
2.2 Arrays #
Key Concept: Array elements are stored contiguously in memory with adjacent addresses.
char v2[3] = {'w','u','p'};
int v3[3] = {11,22,33};
Array Characteristics:
- Fixed number of elements
- Fixed data types (each element occupies same memory size)
#include <stdio.h>
int main() {
char v3[] = "calleng";
printf("Position 0 value: %c, memory address: %p \n", v3[0], &v3[0]);
printf("Position 1 value: %c, memory address: %p \n", v3[1], &v3[1]);
printf("Position 2 value: %c, memory address: %p \n", v3[2], &v3[2]);
return 0;
}
#include <stdio.h>
int main(int argc, char const *argv[]) {
int v3[] = {11, 22, 33, 44, 55, 66}; // Each integer occupies 4 bytes
printf("Position 0 value: %d, memory address: %p \n", v3[0], &v3[0]); // 0x00000
printf("Position 1 value: %d, memory address: %p \n", v3[1], &v3[1]); // 0x00004
printf("Position 2 value: %d, memory address: %p \n", v3[2], &v3[2]); // 0x00008
return 0;
}
Array Variable as Pointer
#include <stdio.h>
int main(int argc, char const *argv[]) {
// Character array
char v3[] = {'c', 'a', 'l', 'l', 'e', 'n', 'g'};
printf("v3 value: %p \n", v3); // Array name = first element address
printf("v3 address: %p \n", &v3); // Array address
printf("v3[0] address: %p \n", &v3[0]); // First element address
printf("v3[1] address: %p \n", &v3[1]); // Second element address
return 0;
}
2.3 Integer Data Types #
Memory Allocation:
short: 2 bytesint: 4 byteslong: 8 bytes
#include <stdio.h>
int main(int argc, char const *argv[]) {
// Signed vs Unsigned
// signed short v4[] = {11, 22, 33}; // Default: signed
// unsigned short v4[] = {11, 22, 33}; // Unsigned
short v4[] = {-11, 22, 33};
printf("Value: %d, memory address: %p\n", v4[0], &v4[0]);
printf("Value: %d, memory address: %p\n", v4[1], &v4[1]);
return 0;
}
2.4 Pointers #
Pointer Concept Visualization #
Memory Layout:
Memory Location
$
0x7ffd257fceac 666 v1 points here
0x7ffd257fcea0 0x7ffd... v2 points here (stores v1's address)
Basic Pointer Syntax #
int v1 = 666;
int* v2 = &v1; // & operator: Get memory address (8 bytes on 64-bit systems)
#include <stdio.h>
int main() {
int v1 = 666;
int* v2 = &v1;
printf("v1 value: %d, memory address: %p \n", v1, &v1);
printf("v2 value: %p, memory address: %p \n", v2, &v2);
return 0;
}
Output:
v1 value: 666, memory address: 0x7ffd257fceac
v2 value: 0x7ffd257fceac, memory address: 0x7ffd257fcea0
int v1 = 666;
int* v2 = &v1;
// If you have a pointer variable v2 storing a memory address,
// how do you get the value at that address?
int v3 = *v2; // 666 (dereference operator)
Key Pointer Operators:
&variable: Get memory address of variable*pointer: Get value stored at memory address pointed to by pointer
#include <stdio.h>
int main() {
int v1 = 666;
int* v2 = &v1;
int* v3 = &v1;
v1 = 999;
printf("v2 pointer value: %d \n", *v2); // 999
printf("v3 pointer value: %d \n", *v3); // 999
return 0;
}
#include <stdio.h>
int main() {
int v1 = 666;
int* v2 = &v1;
int* v3 = &v1;
printf("v2 pointer value: %d \n", *v2); // 666
printf("v3 pointer value: %d \n", *v3); // 666
*v2 = 888; // Modify value through pointer
printf("v2 pointer value: %d \n", *v2); // 888
printf("v3 pointer value: %d \n", *v3); // 888
printf("v1 value: %d \n", v1); // 888
return 0;
}
#include <stdio.h>
void modifyValue(int *arg) {
*arg = 555;
}
int main() {
int v1 = 666;
modifyValue(&v1); // Pass address
printf("v1 value after modification: %d \n", v1); // 555
return 0;
}
Critical Warning for Reverse Engineers #
When analyzing C code, be cautious about function calls:
v1 = 123;
doEncrypt(&v1); // DANGEROUS: v1's value may change after this call
// Later in code, v1 might not be 123 anymore!
vs.
v1 = 123;
doEncrypt(v1); // SAFE: v1 will remain 123
// v1 is guaranteed to still be 123
Pointer Summary #
- Essence: Pointers are data types representing memory addresses, enabling multiple variables to reference the same value
- Pointer Types:
int*,char*,short*, etc. - Key Operators:
&(address of) and*(dereference) - Memory Usage: One pointer occupies 8 bytes on 64-bit systems
- Purpose: Memory efficiency and shared data access
2.5 Advanced Pointer Concepts #
Pointer Arithmetic Examples #
#include <stdio.h>
int main() {
char v34[] = {'a', 'e', 'x'};
char *v28 = v34; // Points to first element
printf("v34[0]: %c, address: %p \n", v34[0], &v34[0]);
printf("v34[1]: %c, address: %p \n", v34[1], &v34[1]);
printf("v34[2]: %c, address: %p \n", v34[2], &v34[2]);
printf("v28 initial value: %p \n", v28);
v28 += 1;
printf("v28 after +1: %p \n", v28);
v28 += 1;
printf("v28 after +2: %p \n", v28);
return 0;
}
#include <stdio.h>
int main() {
char v34[] = {'a', 'e', 'x'};
char *v28 = v34;
printf("v28: %p, value: %c \n", v28, *v28);
v28 += 1;
printf("v28: %p, value: %c \n", v28, *v28);
v28 += 1;
printf("v28: %p, value: %c \n", v28, *v28);
return 0;
}
#include <stdio.h>
int main() {
char v34[] = {'a', 'e', 'x'};
char *v28 = v34;
v28 += 1; // Point to second element
*v28 = 'b'; // Modify it
printf("v34[0]: %c \n", v34[0]); // 'a'
printf("v34[1]: %c \n", v34[1]); // 'b' (modified)
printf("v34[2]: %c \n", v34[2]); // 'x'
return 0;
}
String Manipulation Examples #
#include <stdio.h>
#include <string.h>
int main() {
char name[] = "calleng";
// Check if substring "pe" exists in name
char *res = strstr(name, "pe");
if (res == NULL) {
printf("Not found\n");
} else {
printf("Found at position: %p\n", res);
}
return 0;
}
// Application: Anti-debugging techniques (checking for "frida/tmp" strings)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
char name[] = "wupeiqi";
printf("Original: %s, address: %p\n", name, name);
// Allocate memory for copy (+1 for null terminator)
char *newName = malloc(strlen(name) + 1);
// Copy content
strcpy(newName, name);
printf("Copy: %s, address: %p\n", newName, newName);
// Free allocated memory
free(newName);
return 0;
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
char name[] = "Joe";
char role[] = "Bidden";
// Allocate sufficient memory
char *result = malloc(strlen(name) + strlen(role) + 1);
// Concatenate strings
strcpy(result, name);
strcat(result, role);
printf("Result: %s\n", result); // "JoeBidden"
free(result);
return 0;
}
2.6 Pointer to Pointer #
Concept: Pointers can point to other pointers, creating multiple levels of indirection.
Memory Layout:
Level 1 (Data)
$
0x1000 666 v1 points here
0x2000 0x1000 v2 points here (stores v1's address)
0x3000 0x2000 v3 points here (stores v2's address)
int v1 = 666;
int* v2 = &v1; // Single pointer
int* v3 = &v1; // Single pointer
int** v4 = &v2; // Pointer to pointer
int*** v5 = &v4; // Pointer to pointer to pointer
Essence: Pointers enable data manipulation based on memory addresses, providing powerful control over program memory.
2.7 Structures #
Structures allow grouping of related data under a single name.
#include <stdio.h>
struct Person {
char name[30];
int age;
};
int main() {
struct Person v1 = {"Obama", 18};
struct Person v2 = {"Trump", 3};
struct Person v3 = {"Kelinton", 31};
printf("Name: %s\n", v1.name);
return 0;
}
#include <stdio.h>
struct Person {
char name[30];
int age;
};
int main() {
struct Person v1 = {"elop of Nokia CEO", 18};
// Direct member access
printf("Name: %s \n", v1.name);
// Pointer to structure
struct Person *pp = &v1;
// Access through pointer with dereference
printf("Name: %s \n", (*pp).name);
// Access through pointer with arrow operator
printf("Name: %s \n", pp->name);
return 0;
}
Linked Lists Implementation
Visualization:
11 22 33 NULL
struct Node {
int data;
struct Node *next; // Pointer to next node
};
// Node creation
struct Node v3 = {33, NULL};
struct Node v2 = {22, &v3};
struct Node v1 = {11, &v2};
// Access patterns
v1.data; // 11
(*v1.next).data; // 22
v1.next->data; // 22
v1.next->next->data; // 33
#include <stdio.h>
struct Person {
int data;
struct Person *next;
};
int main(int argc, char const *argv[]) {
struct Person p3 = {33};
struct Person p2 = {22, &p3};
struct Person p1 = {11, &p2};
printf("p1 data: %d\n", p1.data);
printf("p2 data: %d\n", p1.next->data);
printf("p3 data: %d\n", p1.next->next->data);
return 0;
}
Visualization:
NULLNULL
11 22 33
#include <stdio.h>
struct Person {
int data;
struct Person *next; // Next node pointer
struct Person *prev; // Previous node pointer
};
int main() {
struct Person p3 = {33};
struct Person p2 = {22};
struct Person p1 = {11};
// Set forward links
p1.next = &p2;
p2.next = &p3;
// Set backward links
p2.prev = &p1;
p3.prev = &p2;
// Forward traversal
printf("Forward: %d -> %d -> %d\n",
p1.data, p1.next->data, p1.next->next->data);
// Backward traversal
printf("Backward: %d -> %d -> %d\n",
p3.data, p3.prev->data, p3.prev->prev->data);
return 0;
}
Visualization:
11 22 33
#include <stdio.h>
struct Person {
int data;
struct Person *next;
struct Person *prev;
};
int main() {
struct Person p3 = {33};
struct Person p2 = {22};
struct Person p1 = {11};
// Create circular links
p1.next = &p2; p1.prev = &p3;
p2.next = &p3; p2.prev = &p1;
p3.next = &p1; p3.prev = &p2;
// Circular traversal
printf("Circular traversal:\n");
printf("Position 1: %d\n", p1.data);
printf("Position 2: %d\n", p1.next->data);
printf("Position 3: %d\n", p1.next->next->data);
printf("Back to 1: %d\n", p1.next->next->next->data);
return 0;
}
2.8 Preprocessor and Header Files #
Preprocessor: Runs before program compilation.
#include <stdio.h>
#define ME 2024
#define SIZE 07
int main() {
int data = 19;
printf("%d-%d-%d \n", ME, SIZE, data);
return 0;
}
#include <stdio.h>
#define ADD(x1, x2) (x1 + x2 + 100)
int main() {
int data = ADD(11, 22); // 11 + 22 + 100
printf("Result: %d \n", data);
return 0;
}
#include <stdio.h>
#define DB(x1, x2) (x1 * x2)
int main() {
int data = DB(2 + 1, 4); // Becomes: (2 + 1 * 4) = 6, not 12
printf("Result: %d \n", data);
return 0;
}
Multi-file Projects
Project Structure:
project/
main.c
utils.c
utils.h
// Function declaration
int plus(int v1);
int plus(int v1) {
return v1 + 100;
}
Main File (main.c):
#include <stdio.h>
#include "utils.h" // Include custom header
int main() {
int data = plus(100);
printf("Result: %d \n", data);
return 0;
}
Application Note: For JNI development, we include C standard library headers and JNI-specific headers to access native functionality.
3. Summary and Key Concepts #
Core Takeaways #
- Memory Management: Understanding pointers is crucial for reverse engineering
- Data Structures: Arrays, structures, and linked lists form the foundation
- Type System: Strong typing with explicit memory allocation
- Preprocessor: Macro substitution before compilation
- Multi-file Projects: Header files enable modular development
For Reverse Engineers #
- Pointer Analysis: Essential for understanding data flow
- Memory Layout: Critical for buffer overflow and memory corruption analysis
- Structure Definitions: Key to reverse engineering complex data formats
- JNI Integration: Important for Android native code analysis
Next Steps #
This foundation enables:
- Advanced reverse engineering techniques
- Binary exploitation analysis
- Mobile security assessment
- Custom tool development for security research
