Locating a Short-Video App's Request Signature: Tracing the sig3 Call Chain into Native Code

About the author. I’m Colin Xiao — an independent reverse engineer, mostly working solo on contract jobs. I dig into compiled code of every kind, including but not limited to: Android and iOS apps, x86/x64/ARM native binaries, packers and obfuscators, DRM and license checks, network protocols and APIs, crypto and signature algorithms, malware, game binaries, and embedded/IoT firmware. I also handle large-scale data collection and vulnerability research. If you have any project in mind, I’d be glad to take it on — whatever it is, as long as it’s within my wheelhouse. Just reach out. You can reach me on X at @colinxiao_lab or on Telegram at https://t.me/ColinXiao666.

This research is published for educational and defensive purposes. The target sample is anonymized throughout.


Locating a Short-Video App’s Request Signature: Tracing the sig3 Call Chain into Native Code

The goal this time wasn’t to reverse an algorithm — it was to find it. The target app signs its requests with a sig3 parameter, and the signing logic eventually sinks into a heavily OLLVM-obfuscated native library. I’m still climbing the learning curve, so this post only covers the first, often-skipped skill: walking a signature from a URL parameter all the way down to the native call that computes it. Reversing the OLLVM comes later.

Finding the entry point

The target version is 7.2.4.13202. Charles revealed the full name of the parameter:

Charles showing the parameter

It’s _NS_sig3. Dropping the APK into jadx-gui, I did the classic string search:

String search

Search results

The highlighted region shows a3 coming from a2.a(...), with str2 being the signature result (from the code above it) and b2 a URL that’s been through some b() processing. Ignore what b2 holds for now — follow where a is ultimately called.

Walking the call chain

String a3 = a2.a(b2 + str2);

Following a’s declaration:

a’s declaration

It’s an interface. Right-click → find usages:

Find usages

Exactly one class implements it. In, then through getSigWrapper, then into j.c(str):

j.c(str)

Still descending: String atlasSign = KSecurity.atlasSign(str);. Follow that declaration:

atlasSign

Double-clicking str to see its references:

str references

It calls d().a(str). Follow into that implementation — it passes a 2 and a false:

The d().a(str) call

Here it constructs an i and populates it field by field. Stripping the noise:

i iVar = new i();
iVar.a(KSecurity.getkSecurityParameterContext().getAppkey());
iVar.a((Map<String, String>) null);
iVar.b(0);
iVar.a(z2);
iVar.b(str.getBytes("UTF-8"));
if (i.a(this.a).b().a(iVar, "0335") && iVar.j() != null) {

The real work happens in i.a(this.a).b().a(iVar, "0335"). Following it:

The interface

Another interface. Find usages, then the concrete a(i, string):

The implementation

Reading it:

String trim = new String(iVar.i()).trim();
String str2 = (String) this.a.getRouter().a(10405, new String[]{trim}, KSecurity.getkSecurityParameterContext().getAppkey(), -1, false, KSecurity.getkSecurityParameterContext().getContext(), null, Boolean.valueOf(iVar.b()));
iVar.c(str2.getBytes("UTF-8"));

Recall the parameters were packed into iVar. Here it pulls the content-to-sign back out with iVar.i(), then calls this.a.getRouter().a — passing a lot of values, so this is likely the final dispatch. Following a:

Another interface

Interface again. Find usages, then the implementation:

The implementation

return JNICLibrary.doCommandNative(i, objArr);

There it is: the hand-off to native code.

Hooking the boundary

Rather than reproduce the last two arguments (many of them fixed constants), I picked a hook point just above the native call:

com.kuaishou.android.security.a.a.a.a(com.kuaishou.android.security.kfree.c.i, java.lang.String)

The hook point

The two arguments are the URL path and the signature — confirming the earlier merge of str + str2 was the path plus the sig.

Working backward to a’s parameters, here’s what actually gets passed to native:

# Value Meaning
1 10405 command/route id
2 new String[]{trim} the content to sign (what my hook printed)
3 KSecurity.getkSecurityParameterContext().getAppkey() a fixed app key
4 -1 —
5 false a flag
6 KSecurity.getkSecurityParameterContext().getContext() the app context
7 null —
8 Boolean.valueOf(iVar.b()) the false passed in earlier

The app key (parameter 3) is a fixed value found through its own usages:

The app key

Parameter 8 is just the boolean we saw set at the start; b() reads back that assignment.

With all the parameters identified, I stopped at the .so boundary. The native layer is riddled with OLLVM, so the remaining move — for now — is calling it actively via hook rather than reversing it.

Locating the .so

Even finding which shared object loads here is non-trivial, so here’s how I traced it. JNICLibrary.doCommandNative’s definition doesn’t show a System.loadLibrary:

doCommandNative

doCommandNative, continued

Find usages of b, and there’s one new site:

The new site

Follow it:

Following b

The surrounding text shows it calls boolean a = f.a(context) here to trigger the load — the log even hints at timing the load:

f.a(context)

And there’s the name: kwsgmain, with a retry load so ok message right below it. Checking lib/, the file exists. Tracing that string’s call sites confirms it:

Confirmed the .so name

Loading kwsgmain.so into IDA and looking at JNI_OnLoad… that’s where I hit pause for now:

JNI_OnLoad

The next milestone is working through the OLLVM. The locating half — the part that trips up beginners and that this post is about — is done: a signature parameter in a URL, traced through a chain of interfaces and dispatch layers, down to a named native function and its .so. Everything after that is a different skill set.


Colin Xiao — I take on contract reverse-engineering work, and writeups like this are how I show what I can do. If you need help with anything in this space — including but not limited to the areas I cover above — I’m available. No project is too big or too small — if it’s reverse engineering and I can do it, I’ll take it. DM me on X (@colinxiao_lab) or Telegram (@ColinXiao666).

Get in touch