I've found an C++ API there was called from Java JNI. The libcma.so contains the method Java_com_smule_android_network_core_NetworkUtils_makeDigest which will create an Digest - I Think it's MD5.
Here is the reversed source:
https://raw.githubusercontent.com/Bizarrus/Sing/master/cma.cpp
And here is the JNI definition:
https://github.com/Bizarrus/Sing/blob/7ef0e748bae710bde2d07111bd66f1e7fc0810b4/com/smule/android/network/core/NetworkUtils.java#L53
Can anyone tell me, which Algorithm will be used?
Edit
Following HTTP-Request will be made:
POST http://api-sing.smule.com/v2/login/guest?msgId=1776&appVersion=5.7.5&app=sing_google&appVariant=1&digest=179645edb702ce4a57197141522d848145f8861f HTTP/1.1
User-Agent: com.smule.singandroid/5.7.5 (6.0,F3311,de_DE)
Content-Type: application/json; charset=UTF-8
Content-Length: 501
Host: api-sing.smule.com
Connection: Keep-Alive
Accept-Encoding: gzip
{
"common": {
"advId": "e133b6d9-25b1-4651-b4b8-94d80fa25ed9",
"automaticLogin": true,
"device": {
"carrierCountry": "de",
"country": "DE",
"deviceId": "a:e133b6d9-25b1-4651-b4b8-94d80fa25ed9",
"deviceType": "AND",
"googlePlayServices": "12.6.85 (040306-197041431)",
"hasMail": true,
"lang": "de",
"limitAdTrack": false,
"locale": "de_DE",
"machine": "F3311",
"manufacturer": "Sony",
"os": "6.0",
"product": "F3311",
"screenSize": "normal",
"script": ""
},
"pulp": 17,
"tzOffset": 3600,
"vorgom": true
},
"forceNewPlayer": true,
"lookupAccount": true
}
with following Response:
HTTP/1.1 200 OK
Server: nginx/1.11.5
Date: Sun, 24 Jun 2018 16:27:31 GMT
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
X-Smule-Host: a155.sf.smle.co
X-Smule-Digest: 64dc15893bbf43240798c73ae652bfb80e848f57
Set-Cookie: L=N; Max-Age=172800; Expires=Tue, 26 Jun 2018 16:27:31 GMT; Path=/; Domain=.smule.com; Secure
Cache-Control: no-cache
Content-Encoding: gzip
{
"data": {
"loginResult": {
"elControl": {
"npt": false
},
"handleNew": false,
"handlePrefill": true,
"language": "de",
"loginCount": 1,
"playerId": 1762444898,
"playerNew": true,
"playerNewlyRegistered": false,
"playerStat": {
"installDate": 1529857651000
},
"policyUrl": "https://www.smule.com/privacy/embed/20180523",
"policyVersion": "PRIVACY_POLICY_20180523",
"serverTime": 1529857651,
"sessionToken": "g4_10_wma5HOX13kDeho2gvEuIQyf5EnUaAp0Uw3C24O5w9s9xUB1U0JOC0w==",
"sessionTtl": 86400,
"showEmailOpt": true,
"termUrl": "https://www.smule.com/termsofservice/embed/20180523"
},
"settings": {}
},
"status": {
"code": 0,
"message": "ok",
"version": 1
}
}
On the POST request, the Query-Argument **digestis theHashof the request. TheseDigestwill be created byJava_com_smule_android_network_core_NetworkUtils_makeDigest(i think). I don't know, which data will be used for theHashing-Algorithm- I've tried to reproduce theDigest` with following parameters:
Only all Query-Parameters, sorted alphabetically, exclude the digest-Argument (These method will be used on the Website under JavaScript`
Only the Content-Body, in JSON-String (tried as pretty-printed and comprimized)
Content-Body and Query-Parameters expected as above
I've tried some combinations of data to reproduce the digest, but i've found no solutions, which data reprensent the original digest.
From the Response on the X-Smule-Digest header will be hashed by MD5, that i had reproducted last week. Here (in PHP) a working sample to calculate the X-Smule-Digest:
$string = sprintf('%s=%s', 'secret', 'M=|ZUyMu^-qWb}VL^jJd}Mv)8y%bQWXf>IFBDcJ>%4zg2Ci|telj`dVZ#');
$string .= sprintf('&%s=%s', 'path', '/user/json/login'); // sample url-path
$string .= sprintf('&%s=%s', 'csrf', $this->csrf); // from Website, <meta content="([^"]+)" name="csrf-token" or from Header "x-csrf-token"
$string .= sprintf('&%s', http_build_query($this->data)); // POST-Body
$array = explode('&', $string);
sort($array); // Alphabetical sort
$x_smule_digest = md5(implode('&', $array));
1 . The digest you indicate is 40 characters long, and contains numbers from 0 to 9 and letters from a to f. So it is 40 hex values. So it is most probably created using SHA-1 digest function.
2 . The C++ function you indicate has the following signature :
int32_t Java_com_smule_android_network_core_NetworkUtils_makeDigest(int32_t * a1, int32_t a2, int32_t a3)
And the corresponding signature in the Java source code is :
private static native String makeDigest(String str, byte[] bArr);
The first argument a1 may be a pointer reserved by the JNI environment. So, input a1 input set aside, the function takes two arguments : a string and an array of byte, and return one string : the digest. The C++ code itself is too complex for me to dig in.
3 . This JNI function is used twice in the Java file you indicate. In functions m18107a and m18106a. Let's add some comments to the source code :
// Function that takes three arguments :
// - a list of pairs of string we will call the "json pairs"
// - a string we will call the "secret"
// - an other string we will call the "optional token"
// and return the returned string of makeDigest which may be the digest.
public static String m18107a(List<Pair<String, String>> list, String str, String str2)
{
// Sort the json pairs alphabetically according to the first string of each pairs.
// (Check the source code of C35442 to verify my statement, I may be wrong )
Collections.sort(list, new C35442());
// Create a single string, that concatenate all the json pairs.
// ( I guess { age : 21, name : "bob" } becomes "age21namebob" )
// We will call that the "digest input"
StringBuilder stringBuilder = new StringBuilder();
for (Pair pair : list) {
stringBuilder.append((String) pair.first).append((String) pair.second);
}
// Append the "digest input" just created with the "optional token" if it exists.
if (str2 != null) {
stringBuilder.append(str2);
}
// Send the "secret" string, and the "digest input" as BYTES (may be important)
// Return the digest computed as returned string of the function.
return makeDigest(str, stringBuilder.toString().getBytes());
}
// Function that takes four arguments :
// - a string str
// - a number j
// - a string str2
// - a string str3 (can be null, as seen later)
// and return the returned string of makeDigest which may be the digest.
public static String m18106a(String str, long j, String str2, String str3)
{
// Create a single string, that concatenate j and str2.
// We will call that the "digest input"
StringBuilder append = new StringBuilder().append("").append(j).append(str2);
// If str3 is null, make it empty.
if (str3 == null) {
str3 = "";
}
// Send the "secret" string, and the "digest input" concatenated with str3, as BYTES (may be important).
// Return the digest computed as returned string of the function.
return makeDigest(str, append.append(str3).toString().getBytes());
}
It will be hard to go further. Try to dig few things :
Where are these functions m18107a and m18106a called ? Can you at least find what are the inputs of these ? So you can play with the good inputs.
Can you put breakpoints around these functions ? To track inputs and outputs and try to reproduce ?
From all the suspected inputs, can you find a way to modify only one per one and check if the digest changes ? To eliminate suspected inputs.
Related
Description:
I've created a simple smart contract where I'd enter in basic information using the enterAutomobiles() function. To read the data, I use getAllAutomobiles() function. To remove the data, I'd use the this function removeAutomobile(uint i).
Issue:
When data is retrieved using the getAllAutomobiles() function, the resulting retrieved data is a mixture of unicode characters like the following: ``` Àà Àà à 111test123test123
Question: Why is the above unicode characters occurring?
Question: How can I present the data to appear in this format?
[ { vinNumber: 1, miscId: "11", licenseInfo: "testtest", vehicleDescription: "testtest123" } ];
When I want to remove data using the removeAutomobile(uint index) function, where index = 0, the data at index = 0 is deleted. However, when I invoke the removeAutomobile(uint index) where index = 0 again, there was no error message indicating that the index was previously removed. What can I do in this case?
struct AllAutomobiles {
uint carId;
uint vinNumber;
string miscId;
string licenseInfo;
string vehicleDescription;
}
AllAutomobiles [] public cars;
uint public autoMobileCount = 0;
//Enter automobiles
function enterAutomobiles(uint vinNumber, string memory miscId, string memory licenseInfo, string memory vehicleDescription) public onlyOwner {
cars.push(AllAutomobiles(autoMobileCount, vinNumber, miscId, licenseInfo, vehicleDescription));
autoMobileCount++;
}
//get all automobiles
function getAllAutomobiles() public view returns (AllAutomobiles[] memory) {
return cars;
}
function removeAutomobiles(uint index) public {
if (index >= cars.length) return revert('Automobile does not exist!');
if (index == cars[index].carId) {
delete cars[index];
autoMobileCount--;
}
}
Your enterAutomobiles function is wrong. You have to push an "AllAtomobiles" struct in your array, not an "GiveawayWinners"! That's why the getAllAutomobiles function is returning strange characters.
And by the way, in my experience, I can only advise you to change your struct name to something more clear !
Edit: When running your new code everything seems good to me :
>>> contract = test.deploy({"from":accounts[0]})
Transaction sent: 0x18d498309e787b6c65bad40010461a8514a373b2361b74a952a221d201de1a60
Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 0
test.constructor confirmed Block: 1 Gas used: 672896 (5.61%)
test deployed at: 0x3194cBDC3dbcd3E11a07892e7bA5c3394048Cc87
>>> contract.enterAutomobiles("123", "abc", "def", "blue", {"from":accounts[0]})
Transaction sent: 0x15cb272745d39287220e9286bd2542fb1c3e654032c83323b8e918207b0c318c
Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 1
test.enterAutomobiles confirmed Block: 2 Gas used: 151166 (1.26%)
<Transaction '0x15cb272745d39287220e9286bd2542fb1c3e654032c83323b8e918207b0c318c'>
>>> contract.getAllAutomobiles()
((0, 123, 'abc', 'def', 'blue'))
I'd like to set boolean validation, it means it only permit 0 or 1.
entity.ts
#Column('int')
isLastdate: number;
I set above rule by following regular expression
dto.ts
const boolRegex = /^[01]$/
#IsNotEmpty()
#IsInt()
#Matches(boolRegex,{
message:`isLastdate must be bool (0 or 1)`
})
isLastdate: number;
I throw following json to the api-server
{
"userId":1,
"title":"mytest",
"date":"2000-12-31",
"isLastdate":1,
"beginTime":"11:59",
"endTime":"23:40",
"place":"Tokyo",
"labelCd":1
}
But the response is following.
Are there anything wrong with my validation ?
{
"statusCode": 400,
"message": [
"isLastdate must be bool (0 or 1)"
],
"error": "Bad Request"
}
Instead of type int, try going for boolean, both in your dto and entity
#Column('boolean').
If you really need 0 or 1 in request, map them to boolean while saving in the database.
use #IsNumberString instead #IsInt
Before you mark this as duplicate, note that others are asking about the error Invalid column index undefined. ... or Invalid column index 5. Should be an integer in the range [0-4]. But no. Mine is "3 should be an integer in the range of [0-3]." Also, the table does work without the formatter.format() line (just no formatting).
google.charts.load('current', {'packages':[data.chartType.toLowerCase()]});
google.charts.setOnLoadCallback(function(){
var googleData = new google.visualization.DataTable();
for (var h in data.headers) {
googleData.addColumn(data.headers[h].type, data.headers[h].html);
if (data.headers[h].format) {
var formatter = new google.visualization.NumberFormat(data.headers[h].format);
console.log(data.headers[h].format);
formatter.format(googleData, h); // Errors Here
}
}
/* ... Add Rows ... Draw Chart ... */
}
The header in question looks like this:
header[3] = {
"html": "Total Amount",
"source": "total_amount",
"type": "number",
"format": {
"negativeColor": "#F05840", //orange
"negativeParens": true,
"pattern": "#,###",
"prefix": "$",
"suffix": "",
}
}
I can't figure out why it would be erroring.
Please forgive me for any typos here, I had to hand-edit the spacing and remove my company's specific info upon pasting the code here.
Edit
WhiteHat is correct in that my h variable was a string instead of an integer, and calling parseInt did remove that error. However, instead of calling parseInt on the formatter and wherever else it's needed, I got rid of my for (var h in data.headers) calls and went with the bog-standard for (var h = 0; h < data.headers.length; h++). Although more verbose with more room for typos, it's far more standardized and predictable.
I'm still having issues with GoogleCharts NumberFormatter, but that's for another round of research and questions, not this one.
make sure you're passing a number (3),
and not a string ('3'),
by using --> parseInt...
e.g.
formatter.format(googleData, parseInt(h)); // <-- here
I try to create simple file upload service in C++. I get all user request body as one big string. User can upload any type of data. I need to get only user file contents from request boby string.
so for example now I have next code working with my service API provider:
std::cout << "Request body: " << request->body << std::endl << "Request size: " << request->body.length() << std::endl;
and this would print as:
Request body: ------WebKitFormBoundaryAZlJcLinxYi6OCzX
Content-Disposition: form-data; name="datafile"; filename="crossdomain.xml"
Content-Type: text/xml
я╗┐<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-
omain-policy.dtd">
<cross-domain-policy>
<allow-access-from domain="*" to-ports="*" />
</cross-domain-policy>
------WebKitFormBoundaryAZlJcLinxYi6OCzX--
Request size: 411
So I need to get from request->body (which is string) all data from first /r/n/r/n until last line -2 lines. How to do such thing with string in C++?
This isn't the most elegant approach, but one option would be to do something like this:
std::string contents = /* ... get the string ... */
/* Locate the start point. */
unsigned startPoint = contents.find("\r\n\r\n");
if (startPoint == string::npos) throw runtime_error("Malformed string.");
/* Locate the end point by finding the last newline, then backing up
* to the newline before that.
*/
unsigned endPoint = contents.rfind('\n');
if (endPoint == string::npos || endPoint == 0) throw runtime_error("Malformed string.");
endPoint = contents.rfind('\n', endPoint - 1);
if (endPoint == string::npos) throw runtime_error("Malformed string.");
/* Hand back that slice of the string. */
return std::string(contents.begin() + startPoint, contents.begin() + endPoint);
You can use regular expressions for that. This page has some nice c++ examples: http://www.math.utah.edu/docs/info/libg++_19.html
I am writing an arduino library to post http request on web.
I am using the String class from http://arduino.cc/en/Tutorial/TextString
My code is behaving strangely when I am referring to my defined string objects after a function call.
Here actually I am trying to get the body of my GET request and removing the http headers from the http GET request's response.
Following is the description:
Method Call:
String body;
if(pinger.Get(host,path,&body))
{
Serial.println("Modified String Outside :");
Serial.println(body);
Serial.println();
Serial.println("Modified String Outside Address");
Serial.println((int)&body);
}
Output
Modified String Outside :
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html
Content-Length: 113
Date: Wed, 13 Jan 2010 14:36:28 GMT
<html>
<head>
<title>Ashish Sharma
</title>
</head>
<body>
Wed Jan 13 20:06:28 IST 2010
</body>
</html>
Modified String Outside Address
2273
Method Description:
bool Pinger::Get(String host, String path, String *response) {
bool connection = false;
bool status = false;
String post1 = "GET ";
post1 = post1.append(path);
post1 = post1.append(" HTTP/1.1");
String host1 = "Host: ";
host1 = host1.append(host);
for (int i = 0; i < 10; i++) {
if (client.connect()) {
client.println(post1);
client.println(host1);
client.println();
connection = true;
break;
}
}
int nlCnt = 0;
while (connection) {
if (client.available()) {
int c = client.read();
response->append((char) c);
if (c == 0x000A && nlCnt == 0) {
nlCnt++;
if (response->contains("200")) {
status = true;
continue;
} else {
client.stop();
client.flush();
break;
}
}
}
if (!client.connected()) {
client.stop();
connection = false;
}
}
response = &response->substring(response->indexOf("\n\r\n"),response->length());
Serial.println("Modified String: ");
Serial.println(*response);
Serial.println();
Serial.print("Modified String Address: ");
Serial.println((int)&response);
return status;
}
Output:
Modified String:
Ø
<html>
<head>
<title>Ashish Sharma
</title>
</head>
<body>
Wed Jan 13 20:06:28 IST 2010
</body>
</html>
Modified String Address: 2259
As can be seen from the example the string reference object is giving me the correct string inside the Get method but the reference of the string contents change when the Get method returns.
If I understood correctly your code, you probably would want to do something like this:
*response = response->substring(response->indexOf("\n\r\n"),response->length());
instead of
response = &response->substring(response->indexOf("\n\r\n"),response->length());
Also there's probably no need to pass in a pointer ( reference would probably make the code look much nicer ).
First off, you are modyfying the address of the string, not the string itself. But the address of the string was passed to the function by value, and thus copied. Modifying it inside the function won’t modify it on the outside.
Secondly, this code here is bad:
response = &response->substring(response->indexOf("\n\r\n"),response->length());
Because it creates a pointer to a temporary object – which means: a dangling pointer because the temporary object will be destroyed after the expression has been evaluated.
What you really want is pass the object by reference (String& response) and modify it, not its pointer:
response = response->substring(response->indexOf("\n\r\n"),response->length());
This should work, provided the String class you use correctly overloads the behaviour of the assignment operator =.
The line
Serial.println((int)&response);
inside your function is wrong response is already a pointer (String * response), with &response you get the pointer to the pointer.
Change it to
Serial.println((int)response);
and you should get the same address as with
Serial.println((int)&body);
where body is a String and &body is the pointer to the string